http://blog.csdn.net/johnsonblog/article/details/7974312

效果

其中使用了贝赛尔曲线原理,关于贝赛尔曲线的知识,推荐大家看下http://blog.csdn.net/hmg25的博客

主函数

[java] view plaincopy
  1. package com.zhang;
  2. import java.io.IOException;
  3. import android.app.Activity;
  4. import android.graphics.Bitmap;
  5. import android.graphics.BitmapFactory;
  6. import android.graphics.Canvas;
  7. import android.os.Bundle;
  8. import android.view.MotionEvent;
  9. import android.view.View;
  10. import android.view.View.OnTouchListener;
  11. import android.view.Window;
  12. import android.view.WindowManager;
  13. import android.widget.Toast;
  14. public class TurnBook extends Activity {
  15. /** Called when the activity is first created. */
  16. private PageWidget mPageWidget;
  17. Bitmap mCurPageBitmap, mNextPageBitmap;
  18. Canvas mCurPageCanvas, mNextPageCanvas;
  19. BookPageFactory pagefactory;
  20. @Override
  21. public void onCreate(Bundle savedInstanceState) {
  22. super.onCreate(savedInstanceState);
  23. requestWindowFeature(Window.FEATURE_NO_TITLE);
  24. getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
  25. WindowManager.LayoutParams.FLAG_FULLSCREEN);
  26. mPageWidget = new PageWidget(this);
  27. setContentView(mPageWidget);
  28. mCurPageBitmap = Bitmap.createBitmap(480, 800, Bitmap.Config.ARGB_8888);
  29. mNextPageBitmap = Bitmap
  30. .createBitmap(480, 800, Bitmap.Config.ARGB_8888);
  31. mCurPageCanvas = new Canvas(mCurPageBitmap);
  32. mNextPageCanvas = new Canvas(mNextPageBitmap);
  33. pagefactory = new BookPageFactory(480, 800);//设置分辨率为480*800
  34. pagefactory.setBgBitmap(BitmapFactory.decodeResource(
  35. this.getResources(), R.drawable.bg));//设置背景图片
  36. try {
  37. pagefactory.openbook("/sdcard/test.txt");//打开文件
  38. pagefactory.onDraw(mCurPageCanvas);//将文字绘于手机屏幕
  39. } catch (IOException e1) {
  40. // TODO Auto-generated catch block
  41. e1.printStackTrace();
  42. Toast.makeText(this, "电子书不存在,请将《test.txt》放在SD卡根目录下",
  43. Toast.LENGTH_SHORT).show();
  44. }
  45. mPageWidget.setBitmaps(mCurPageBitmap, mCurPageBitmap);
  46. mPageWidget.setOnTouchListener(new OnTouchListener() {
  47. @Override
  48. public boolean onTouch(View v, MotionEvent e) {
  49. // TODO Auto-generated method stub
  50. boolean ret=false;
  51. if (v == mPageWidget) {
  52. if (e.getAction() == MotionEvent.ACTION_DOWN) {
  53. //停止动画。与forceFinished(boolean)相反,Scroller滚动到最终x与y位置时中止动画。
  54. mPageWidget.abortAnimation();
  55. //计算拖拽点对应的拖拽角
  56. mPageWidget.calcCornerXY(e.getX(), e.getY());
  57. //将文字绘于当前页
  58. pagefactory.onDraw(mCurPageCanvas);
  59. if (mPageWidget.DragToRight()) {
  60. //是否从左边翻向右边
  61. try {
  62. //true,显示上一页
  63. pagefactory.prePage();
  64. } catch (IOException e1) {
  65. // TODO Auto-generated catch block
  66. e1.printStackTrace();
  67. }
  68. if(pagefactory.isfirstPage())return false;
  69. pagefactory.onDraw(mNextPageCanvas);
  70. } else {
  71. try {
  72. //false,显示下一页
  73. pagefactory.nextPage();
  74. } catch (IOException e1) {
  75. // TODO Auto-generated catch block
  76. e1.printStackTrace();
  77. }
  78. if(pagefactory.islastPage())return false;
  79. pagefactory.onDraw(mNextPageCanvas);
  80. }
  81. mPageWidget.setBitmaps(mCurPageBitmap, mNextPageBitmap);
  82. }
  83. ret = mPageWidget.doTouchEvent(e);
  84. return ret;
  85. }
  86. return false;
  87. }
  88. });
  89. }
  90. }
[java] view plaincopy
  1. package com.zhang;
  2. import java.io.File;
  3. import java.io.IOException;
  4. import java.io.RandomAccessFile;
  5. import java.io.UnsupportedEncodingException;
  6. import java.nio.MappedByteBuffer;
  7. import java.nio.channels.FileChannel;
  8. import java.text.DecimalFormat;
  9. import java.util.Vector;
  10. import android.graphics.Bitmap;
  11. import android.graphics.Canvas;
  12. import android.graphics.Color;
  13. import android.graphics.Paint;
  14. import android.graphics.Paint.Align;
  15. public class BookPageFactory {
  16. private File book_file = null;
  17. private MappedByteBuffer m_mbBuf = null;
  18. private int m_mbBufLen = 0;
  19. private int m_mbBufBegin = 0;
  20. private int m_mbBufEnd = 0;
  21. private String m_strCharsetName = "GBK";
  22. private Bitmap m_book_bg = null;
  23. private int mWidth;
  24. private int mHeight;
  25. private Vector<String> m_lines = new Vector<String>();
  26. private int m_fontSize = 24;
  27. private int m_textColor = Color.BLACK;
  28. private int m_backColor = 0xffff9e85; // 背景颜色
  29. private int marginWidth = 15; // 左右与边缘的距离
  30. private int marginHeight = 20; // 上下与边缘的距离
  31. private int mLineCount; // 每页可以显示的行数
  32. private float mVisibleHeight; // 绘制内容的宽
  33. private float mVisibleWidth; // 绘制内容的宽
  34. private boolean m_isfirstPage,m_islastPage;
  35. // private int m_nLineSpaceing = 5;
  36. private Paint mPaint;
  37. public BookPageFactory(int w, int h) {
  38. // TODO Auto-generated constructor stub
  39. mWidth = w;
  40. mHeight = h;
  41. mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
  42. mPaint.setTextAlign(Align.LEFT);//设置绘制文字的对齐方向
  43. mPaint.setTextSize(m_fontSize);
  44. mPaint.setColor(m_textColor);
  45. mVisibleWidth = mWidth - marginWidth * 2;
  46. mVisibleHeight = mHeight - marginHeight * 2;
  47. mLineCount = (int) (mVisibleHeight / m_fontSize); // 可显示的行数
  48. }
  49. public void openbook(String strFilePath) throws IOException {
  50. book_file = new File(strFilePath);
  51. long lLen = book_file.length();
  52. m_mbBufLen = (int) lLen;
  53. /*
  54. * 内存映射文件能让你创建和修改那些因为太大而无法放入内存的文件。有了内存映射文件,你就可以认为文件已经全部读进了内存,
  55. * 然后把它当成一个非常大的数组来访问。这种解决办法能大大简化修改文件的代码。
  56. *
  57. * fileChannel.map(FileChannel.MapMode mode, long position, long size)将此通道的文件区域直接映射到内存中。但是,你必
  58. * 须指明,它是从文件的哪个位置开始映射的,映射的范围又有多大
  59. */
  60. FileChannel fc=new RandomAccessFile(book_file, "r").getChannel();
  61. //文件通道的可读可写要建立在文件流本身可读写的基础之上
  62. m_mbBuf =fc.map(FileChannel.MapMode.READ_ONLY, 0, lLen);
  63. }
  64. protected byte[] readParagraphBack(int nFromPos) {
  65. int nEnd = nFromPos;
  66. int i;
  67. byte b0, b1;
  68. if (m_strCharsetName.equals("UTF-16LE")) {
  69. i = nEnd - 2;
  70. while (i > 0) {
  71. b0 = m_mbBuf.get(i);
  72. b1 = m_mbBuf.get(i + 1);
  73. if (b0 == 0x0a && b1 == 0x00 && i != nEnd - 2) {
  74. i += 2;
  75. break;
  76. }
  77. i--;
  78. }
  79. } else if (m_strCharsetName.equals("UTF-16BE")) {
  80. i = nEnd - 2;
  81. while (i > 0) {
  82. b0 = m_mbBuf.get(i);
  83. b1 = m_mbBuf.get(i + 1);
  84. if (b0 == 0x00 && b1 == 0x0a && i != nEnd - 2) {
  85. i += 2;
  86. break;
  87. }
  88. i--;
  89. }
  90. } else {
  91. i = nEnd - 1;
  92. while (i > 0) {
  93. b0 = m_mbBuf.get(i);
  94. if (b0 == 0x0a && i != nEnd - 1) {
  95. i++;
  96. break;
  97. }
  98. i--;
  99. }
  100. }
  101. if (i < 0)
  102. i = 0;
  103. int nParaSize = nEnd - i;
  104. int j;
  105. byte[] buf = new byte[nParaSize];
  106. for (j = 0; j < nParaSize; j++) {
  107. buf[j] = m_mbBuf.get(i + j);
  108. }
  109. return buf;
  110. }
  111. //读取上一段落
  112. protected byte[] readParagraphForward(int nFromPos) {
  113. int nStart = nFromPos;
  114. int i = nStart;
  115. byte b0, b1;
  116. // 根据编码格式判断换行
  117. if (m_strCharsetName.equals("UTF-16LE")) {
  118. while (i < m_mbBufLen - 1) {
  119. b0 = m_mbBuf.get(i++);
  120. b1 = m_mbBuf.get(i++);
  121. if (b0 == 0x0a && b1 == 0x00) {
  122. break;
  123. }
  124. }
  125. } else if (m_strCharsetName.equals("UTF-16BE")) {
  126. while (i < m_mbBufLen - 1) {
  127. b0 = m_mbBuf.get(i++);
  128. b1 = m_mbBuf.get(i++);
  129. if (b0 == 0x00 && b1 == 0x0a) {
  130. break;
  131. }
  132. }
  133. } else {
  134. while (i < m_mbBufLen) {
  135. b0 = m_mbBuf.get(i++);
  136. if (b0 == 0x0a) {
  137. break;
  138. }
  139. }
  140. }
  141. //共读取了多少字符
  142. int nParaSize = i - nStart;
  143. byte[] buf = new byte[nParaSize];
  144. for (i = 0; i < nParaSize; i++) {
  145. //将已读取的字符放入数组
  146. buf[i] = m_mbBuf.get(nFromPos + i);
  147. }
  148. return buf;
  149. }
  150. protected Vector<String> pageDown() {
  151. String strParagraph = "";
  152. Vector<String> lines = new Vector<String>();
  153. while (lines.size() < mLineCount && m_mbBufEnd < m_mbBufLen) {
  154. byte[] paraBuf = readParagraphForward(m_mbBufEnd); // 读取一个段落
  155. m_mbBufEnd += paraBuf.length;//结束位置后移paraBuf.length
  156. try {
  157. strParagraph = new String(paraBuf, m_strCharsetName);//通过decode指定的编码格式将byte[]转换为字符串
  158. } catch (UnsupportedEncodingException e) {
  159. // TODO Auto-generated catch block
  160. e.printStackTrace();
  161. }
  162. String strReturn = "";
  163. //去除将字符串中的特殊字符
  164. if (strParagraph.indexOf("\r\n") != -1) {
  165. strReturn = "\r\n";
  166. strParagraph = strParagraph.replaceAll("\r\n", "");
  167. } else if (strParagraph.indexOf("\n") != -1) {
  168. strReturn = "\n";
  169. strParagraph = strParagraph.replaceAll("\n", "");
  170. }
  171. if (strParagraph.length() == 0) {
  172. lines.add(strParagraph);
  173. }
  174. while (strParagraph.length() > 0) {
  175. //计算每行可以显示多少个字符
  176. //获益匪浅
  177. int nSize = mPaint.breakText(strParagraph, true, mVisibleWidth,null);
  178. lines.add(strParagraph.substring(0, nSize));
  179. strParagraph = strParagraph.substring(nSize);//截取从nSize开始的字符串
  180. if (lines.size() >= mLineCount) {
  181. break;
  182. }
  183. }
  184. //当前页没显示完
  185. if (strParagraph.length() != 0) {
  186. try {
  187. m_mbBufEnd -= (strParagraph + strReturn)
  188. .getBytes(m_strCharsetName).length;
  189. } catch (UnsupportedEncodingException e) {
  190. // TODO Auto-generated catch block
  191. e.printStackTrace();
  192. }
  193. }
  194. }
  195. return lines;
  196. }
  197. protected void pageUp() {
  198. if (m_mbBufBegin < 0)
  199. m_mbBufBegin = 0;
  200. Vector<String> lines = new Vector<String>();
  201. String strParagraph = "";
  202. while (lines.size() < mLineCount && m_mbBufBegin > 0) {
  203. Vector<String> paraLines = new Vector<String>();
  204. byte[] paraBuf = readParagraphBack(m_mbBufBegin);
  205. m_mbBufBegin -= paraBuf.length;
  206. try {
  207. strParagraph = new String(paraBuf, m_strCharsetName);
  208. } catch (UnsupportedEncodingException e) {
  209. // TODO Auto-generated catch block
  210. e.printStackTrace();
  211. }
  212. strParagraph = strParagraph.replaceAll("\r\n", "");
  213. strParagraph = strParagraph.replaceAll("\n", "");
  214. if (strParagraph.length() == 0) {
  215. paraLines.add(strParagraph);
  216. }
  217. while (strParagraph.length() > 0) {
  218. int nSize = mPaint.breakText(strParagraph, true, mVisibleWidth,
  219. null);
  220. paraLines.add(strParagraph.substring(0, nSize));
  221. strParagraph = strParagraph.substring(nSize);
  222. }
  223. lines.addAll(0, paraLines);
  224. }
  225. while (lines.size() > mLineCount) {
  226. try {
  227. m_mbBufBegin += lines.get(0).getBytes(m_strCharsetName).length;
  228. lines.remove(0);
  229. } catch (UnsupportedEncodingException e) {
  230. // TODO Auto-generated catch block
  231. e.printStackTrace();
  232. }
  233. }
  234. m_mbBufEnd = m_mbBufBegin;
  235. return;
  236. }
  237. protected void prePage() throws IOException {
  238. if (m_mbBufBegin <= 0) {
  239. //第一页
  240. m_mbBufBegin = 0;
  241. m_isfirstPage=true;
  242. return;
  243. }else m_isfirstPage=false;
  244. m_lines.clear();//Removes all elements from this vector, leaving it empty.
  245. pageUp();
  246. m_lines = pageDown();
  247. }
  248. public void nextPage() throws IOException {
  249. if (m_mbBufEnd >= m_mbBufLen) {
  250. m_islastPage=true;
  251. return;
  252. }else m_islastPage=false;
  253. m_lines.clear();
  254. m_mbBufBegin = m_mbBufEnd;
  255. m_lines = pageDown();
  256. }
  257. public void onDraw(Canvas c) {
  258. if (m_lines.size() == 0)
  259. m_lines = pageDown();
  260. if (m_lines.size() > 0) {
  261. if (m_book_bg == null)
  262. c.drawColor(m_backColor);
  263. else
  264. c.drawBitmap(m_book_bg, 0, 0, null);
  265. int y = marginHeight;
  266. for (String strLine : m_lines) {
  267. y += m_fontSize;
  268. //从(x,y)坐标将文字绘于手机屏幕
  269. c.drawText(strLine, marginWidth, y, mPaint);
  270. }
  271. }
  272. //计算百分比(不包括当前页)并格式化
  273. float fPercent = (float) (m_mbBufBegin * 1.0 / m_mbBufLen);
  274. DecimalFormat df = new DecimalFormat("#0.0");
  275. String strPercent = df.format(fPercent * 100) + "%";
  276. //计算999.9%所占的像素宽度
  277. int nPercentWidth = (int) mPaint.measureText("999.9%") + 1;
  278. c.drawText(strPercent, mWidth - nPercentWidth, mHeight - 5, mPaint);
  279. }
  280. public void setBgBitmap(Bitmap BG) {
  281. m_book_bg = BG;
  282. }
  283. public boolean isfirstPage() {
  284. return m_isfirstPage;
  285. }
  286. public boolean islastPage() {
  287. return m_islastPage;
  288. }
  289. }
[java] view plaincopy
  1. package com.zhang;
  2. import android.content.Context;
  3. import android.graphics.Bitmap;
  4. import android.graphics.Canvas;
  5. import android.graphics.ColorMatrix;
  6. import android.graphics.ColorMatrixColorFilter;
  7. import android.graphics.Matrix;
  8. import android.graphics.Paint;
  9. import android.graphics.Path;
  10. import android.graphics.PointF;
  11. import android.graphics.Region;
  12. import android.graphics.drawable.GradientDrawable;
  13. import android.view.MotionEvent;
  14. import android.view.View;
  15. import android.widget.Scroller;
  16. public class PageWidget extends View {
  17. private static final String TAG = "Book_Turn";
  18. private int mWidth = 480;
  19. private int mHeight = 800;
  20. private int mCornerX = 0; // 拖拽点对应的页脚
  21. private int mCornerY = 0;
  22. private Path mPath0;
  23. private Path mPath1;
  24. Bitmap mCurPageBitmap = null; // 当前页
  25. Bitmap mNextPageBitmap = null;
  26. //PointF:PointF holds two float coordinates
  27. PointF mTouch = new PointF(); // // 拖拽点
  28. PointF mBezierStart1 = new PointF(); // 贝塞尔曲线起始点
  29. PointF mBezierControl1 = new PointF(); // 贝塞尔曲线控制点
  30. PointF mBeziervertex1 = new PointF(); // 贝塞尔曲线顶点
  31. PointF mBezierEnd1 = new PointF(); // 贝塞尔曲线结束点
  32. PointF mBezierStart2 = new PointF(); // 另一条贝塞尔曲线
  33. PointF mBezierControl2 = new PointF();
  34. PointF mBeziervertex2 = new PointF();
  35. PointF mBezierEnd2 = new PointF();
  36. float mMiddleX;
  37. float mMiddleY;
  38. float mDegrees;
  39. float mTouchToCornerDis;
  40. ColorMatrixColorFilter mColorMatrixFilter;
  41. Matrix mMatrix;
  42. float[] mMatrixArray = { 0, 0, 0, 0, 0, 0, 0, 0, 1.0f };
  43. boolean mIsRTandLB; // 是否属于右上左下
  44. float mMaxLength = (float) Math.hypot(mWidth, mHeight);
  45. int[] mBackShadowColors;
  46. int[] mFrontShadowColors;
  47. GradientDrawable mBackShadowDrawableLR;
  48. GradientDrawable mBackShadowDrawableRL;
  49. GradientDrawable mFolderShadowDrawableLR;
  50. GradientDrawable mFolderShadowDrawableRL;
  51. GradientDrawable mFrontShadowDrawableHBT;
  52. GradientDrawable mFrontShadowDrawableHTB;
  53. GradientDrawable mFrontShadowDrawableVLR;
  54. GradientDrawable mFrontShadowDrawableVRL;
  55. Paint mPaint;
  56. Scroller mScroller;
  57. public PageWidget(Context context) {
  58. super(context);
  59. // TODO Auto-generated constructor stub
  60. /**
  61. * Paint类介绍
  62. *
  63. * Paint即画笔,在绘图过程中起到了极其重要的作用,画笔主要保存了颜色,
  64. * 样式等绘制信息,指定了如何绘制文本和图形,画笔对象有很多设置方法,
  65. * 大体上可以分为两类,一类与图形绘制相关,一类与文本绘制相关。
  66. *
  67. * 1.图形绘制
  68. * setARGB(int a,int r,int g,int b);
  69. * 设置绘制的颜色,a代表透明度,r,g,b代表颜色值。
  70. *
  71. * setAlpha(int a);
  72. * 设置绘制图形的透明度。
  73. *
  74. * setColor(int color);
  75. * 设置绘制的颜色,使用颜色值来表示,该颜色值包括透明度和RGB颜色。
  76. *
  77. * setAntiAlias(boolean aa);
  78. * 设置是否使用抗锯齿功能,会消耗较大资源,绘制图形速度会变慢。
  79. *
  80. * setDither(boolean dither);
  81. * 设定是否使用图像抖动处理,会使绘制出来的图片颜色更加平滑和饱满,图像更加清晰
  82. *
  83. * setFilterBitmap(boolean filter);
  84. * 如果该项设置为true,则图像在动画进行中会滤掉对Bitmap图像的优化操作,加快显示
  85. * 速度,本设置项依赖于dither和xfermode的设置
  86. *
  87. * setMaskFilter(MaskFilter maskfilter);
  88. * 设置MaskFilter,可以用不同的MaskFilter实现滤镜的效果,如滤化,立体等       *
  89. * setColorFilter(ColorFilter colorfilter);
  90. * 设置颜色过滤器,可以在绘制颜色时实现不用颜色的变换效果
  91. *
  92. * setPathEffect(PathEffect effect);
  93. * 设置绘制路径的效果,如点画线等
  94. *
  95. * setShader(Shader shader);
  96. * 设置图像效果,使用Shader可以绘制出各种渐变效果
  97. *
  98. * setShadowLayer(float radius ,float dx,float dy,int color);
  99. * 在图形下面设置阴影层,产生阴影效果,radius为阴影的角度,dx和dy为阴影在x轴和y轴上的距离,color为阴影的颜色
  100. *
  101. * setStyle(Paint.Style style);
  102. * 设置画笔的样式,为FILL,FILL_OR_STROKE,或STROKE
  103. *
  104. * setStrokeCap(Paint.Cap cap);
  105. * 当画笔样式为STROKE或FILL_OR_STROKE时,设置笔刷的图形样式,如圆形样式
  106. * Cap.ROUND,或方形样式Cap.SQUARE
  107. *
  108. * setSrokeJoin(Paint.Join join);
  109. * 设置绘制时各图形的结合方式,如平滑效果等
  110. *
  111. * setStrokeWidth(float width);
  112. * 当画笔样式为STROKE或FILL_OR_STROKE时,设置笔刷的粗细度
  113. *
  114. * setXfermode(Xfermode xfermode);
  115. * 设置图形重叠时的处理方式,如合并,取交集或并集,经常用来制作橡皮的擦除效果
  116. *
  117. * 2.文本绘制
  118. * setFakeBoldText(boolean fakeBoldText);
  119. * 模拟实现粗体文字,设置在小字体上效果会非常差
  120. *
  121. * setSubpixelText(boolean subpixelText);
  122. * 设置该项为true,将有助于文本在LCD屏幕上的显示效果
  123. *
  124. * setTextScaleX(float scaleX);
  125. * 设置绘制文字x轴的缩放比例,可以实现文字的拉伸的效果
  126. *
  127. * setTextSkewX(float skewX);
  128. * 设置斜体文字,skewX为倾斜弧度
  129. *
  130. * setTypeface(Typeface typeface);
  131. * 设置Typeface对象,即字体风格,包括粗体,斜体以及衬线体,非衬线体等
  132. *
  133. * setUnderlineText(boolean underlineText);
  134. * 设置带有下划线的文字效果
  135. *
  136. * setStrikeThruText(boolean strikeThruText);
  137. * 设置带有删除线的效果
  138. *
  139. */
  140. mPath0 = new Path();//Path路径对象
  141. mPath1 = new Path();
  142. createDrawable();
  143. mPaint = new Paint();
  144. mPaint.setStyle(Paint.Style.FILL);
  145. //颜色矩阵(ColorMatrix)和坐标变换矩阵(Matrix),对图片进行变换,以拉伸,扭曲等
  146. ColorMatrix cm = new ColorMatrix();
  147. //颜色矩阵,颜色矩阵是一个5x4 的矩阵,可以用来方便的修改图片中RGBA各分量的值,颜色矩阵以一维数组的方式存储如下:
  148. // [ a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t ],他通过RGBA四个通道来直接操作对应颜色
  149. float array[] = { 0.55f, 0, 0, 0, 80.0f, 0, 0.55f, 0, 0, 80.0f, 0, 0,
  150. 0.55f, 0, 80.0f, 0, 0, 0, 0.2f, 0 };
  151. cm.set(array);
  152. //颜色滤镜,就像QQ的在线和离线图片,同一张图片通过颜色滤镜处理,显示不同的效果,可减少图片资源
  153. mColorMatrixFilter = new ColorMatrixColorFilter(cm);
  154. mMatrix = new Matrix();
  155. mScroller = new Scroller(getContext());
  156. mTouch.x = 0.01f; // 不让x,y为0,否则在点计算时会有问题
  157. mTouch.y = 0.01f;
  158. }
  159. /**
  160. * 计算拖拽点对应的拖拽角
  161. */
  162. public void calcCornerXY(float x, float y) {
  163. //将手机屏幕分为四个象限,判断手指落在哪个象限内
  164. if (x <= mWidth / 2)
  165. mCornerX = 0;
  166. else
  167. mCornerX = mWidth;
  168. if (y <= mHeight / 2)
  169. mCornerY = 0;
  170. else
  171. mCornerY = mHeight;
  172. //如果手指落在第一象限或第三象限,也就是右上角或左下角
  173. if ((mCornerX == 0 && mCornerY == mHeight)
  174. || (mCornerX == mWidth && mCornerY == 0))
  175. mIsRTandLB = true;
  176. else
  177. mIsRTandLB = false;
  178. }
  179. public boolean doTouchEvent(MotionEvent event) {
  180. // TODO Auto-generated method stub
  181. if (event.getAction() == MotionEvent.ACTION_MOVE) {
  182. mTouch.x = event.getX();
  183. mTouch.y = event.getY();
  184. /* Android提供了Invalidate和postInvalidate方法实现界面刷新,但是Invalidate不能直接在线程中调用,因为他是违背了单线程模型:
  185. * Android UI操作并不是线程安全的,并且这些操作必须在UI线程中调用。
  186. * invalidate()的调用是把之前的旧的view从主UI线程队列中pop掉
  187. * 而postInvalidate()在工作者线程中被调用
  188. */
  189. this.postInvalidate();
  190. }
  191. if (event.getAction() == MotionEvent.ACTION_DOWN) {
  192. mTouch.x = event.getX();
  193. mTouch.y = event.getY();
  194. // calcCornerXY(mTouch.x, mTouch.y);
  195. // this.postInvalidate();
  196. }
  197. if (event.getAction() == MotionEvent.ACTION_UP) {
  198. //是否触发翻页
  199. if (canDragOver()) {
  200. startAnimation(1200);
  201. } else {
  202. mTouch.x = mCornerX - 0.09f;//如果不能翻页就让mTouch返回没有静止时的状态
  203. mTouch.y = mCornerY - 0.09f;//- 0.09f是防止mTouch = 800 或mTouch= 0 ,在这些值时会出现BUG
  204. }
  205. this.postInvalidate();
  206. }
  207. // return super.onTouchEvent(event);
  208. return true;
  209. }
  210. /**
  211. * 求解直线P1P2和直线P3P4的交点坐标
  212. */
  213. public PointF getCross(PointF P1, PointF P2, PointF P3, PointF P4) {
  214. PointF CrossP = new PointF();
  215. // 二元函数通式: y=ax+b
  216. float a1 = (P2.y - P1.y) / (P2.x - P1.x);
  217. float b1 = ((P1.x * P2.y) - (P2.x * P1.y)) / (P1.x - P2.x);
  218. float a2 = (P4.y - P3.y) / (P4.x - P3.x);
  219. float b2 = ((P3.x * P4.y) - (P4.x * P3.y)) / (P3.x - P4.x);
  220. CrossP.x = (b2 - b1) / (a1 - a2);
  221. CrossP.y = a1 * CrossP.x + b1;
  222. return CrossP;
  223. }
  224. private void calcPoints() {
  225. mMiddleX = (mTouch.x + mCornerX) / 2;
  226. mMiddleY = (mTouch.y + mCornerY) / 2;
  227. mBezierControl1.x = mMiddleX - (mCornerY - mMiddleY)
  228. * (mCornerY - mMiddleY) / (mCornerX - mMiddleX);
  229. mBezierControl1.y = mCornerY;
  230. mBezierControl2.x = mCornerX;
  231. mBezierControl2.y = mMiddleY - (mCornerX - mMiddleX)
  232. * (mCornerX - mMiddleX) / (mCornerY - mMiddleY);
  233. mBezierStart1.x = mBezierControl1.x - (mCornerX - mBezierControl1.x)
  234. / 2;
  235. mBezierStart1.y = mCornerY;
  236. // 当mBezierStart1.x < 0或者mBezierStart1.x > 480时
  237. // 如果继续翻页,会出现BUG故在此限制
  238. if (mTouch.x > 0 && mTouch.x < mWidth) {
  239. if (mBezierStart1.x < 0 || mBezierStart1.x > mWidth) {
  240. if (mBezierStart1.x < 0)
  241. mBezierStart1.x = mWidth - mBezierStart1.x;
  242. float f1 = Math.abs(mCornerX - mTouch.x);
  243. float f2 = mWidth * f1 / mBezierStart1.x;
  244. mTouch.x = Math.abs(mCornerX - f2);
  245. float f3 = Math.abs(mCornerX - mTouch.x)
  246. * Math.abs(mCornerY - mTouch.y) / f1;
  247. mTouch.y = Math.abs(mCornerY - f3);
  248. mMiddleX = (mTouch.x + mCornerX) / 2;
  249. mMiddleY = (mTouch.y + mCornerY) / 2;
  250. mBezierControl1.x = mMiddleX - (mCornerY - mMiddleY)
  251. * (mCornerY - mMiddleY) / (mCornerX - mMiddleX);
  252. mBezierControl1.y = mCornerY;
  253. mBezierControl2.x = mCornerX;
  254. mBezierControl2.y = mMiddleY - (mCornerX - mMiddleX)
  255. * (mCornerX - mMiddleX) / (mCornerY - mMiddleY);
  256. mBezierStart1.x = mBezierControl1.x
  257. - (mCornerX - mBezierControl1.x) / 2;
  258. }
  259. }
  260. mBezierStart2.x = mCornerX;
  261. mBezierStart2.y = mBezierControl2.y - (mCornerY - mBezierControl2.y)
  262. / 2;
  263. mTouchToCornerDis = (float) Math.hypot((mTouch.x - mCornerX),
  264. (mTouch.y - mCornerY));
  265. mBezierEnd1 = getCross(mTouch, mBezierControl1, mBezierStart1,
  266. mBezierStart2);
  267. mBezierEnd2 = getCross(mTouch, mBezierControl2, mBezierStart1,
  268. mBezierStart2);
  269. /*
  270. * mBeziervertex1.x 推导
  271. * ((mBezierStart1.x+mBezierEnd1.x)/2+mBezierControl1.x)/2 化简等价于
  272. * (mBezierStart1.x+ 2*mBezierControl1.x+mBezierEnd1.x) / 4
  273. */
  274. mBeziervertex1.x = (mBezierStart1.x + 2 * mBezierControl1.x + mBezierEnd1.x) / 4;
  275. mBeziervertex1.y = (2 * mBezierControl1.y + mBezierStart1.y + mBezierEnd1.y) / 4;
  276. mBeziervertex2.x = (mBezierStart2.x + 2 * mBezierControl2.x + mBezierEnd2.x) / 4;
  277. mBeziervertex2.y = (2 * mBezierControl2.y + mBezierStart2.y + mBezierEnd2.y) / 4;
  278. }
  279. private void drawCurrentPageArea(Canvas canvas, Bitmap bitmap, Path path) {
  280. mPath0.reset();
  281. mPath0.moveTo(mBezierStart1.x, mBezierStart1.y);
  282. mPath0.quadTo(mBezierControl1.x, mBezierControl1.y, mBezierEnd1.x,
  283. mBezierEnd1.y);
  284. mPath0.lineTo(mTouch.x, mTouch.y);
  285. mPath0.lineTo(mBezierEnd2.x, mBezierEnd2.y);
  286. mPath0.quadTo(mBezierControl2.x, mBezierControl2.y, mBezierStart2.x,
  287. mBezierStart2.y);
  288. mPath0.lineTo(mCornerX, mCornerY);
  289. mPath0.close();
  290. canvas.save();
  291. canvas.clipPath(path, Region.Op.XOR);
  292. canvas.drawBitmap(bitmap, 0, 0, null);
  293. canvas.restore();
  294. }
  295. private void drawNextPageAreaAndShadow(Canvas canvas, Bitmap bitmap) {
  296. mPath1.reset();
  297. mPath1.moveTo(mBezierStart1.x, mBezierStart1.y);
  298. mPath1.lineTo(mBeziervertex1.x, mBeziervertex1.y);
  299. mPath1.lineTo(mBeziervertex2.x, mBeziervertex2.y);
  300. mPath1.lineTo(mBezierStart2.x, mBezierStart2.y);
  301. mPath1.lineTo(mCornerX, mCornerY);
  302. mPath1.close();
  303. mDegrees = (float) Math.toDegrees(Math.atan2(mBezierControl1.x
  304. - mCornerX, mBezierControl2.y - mCornerY));
  305. int leftx;
  306. int rightx;
  307. GradientDrawable mBackShadowDrawable;
  308. if (mIsRTandLB) {
  309. leftx = (int) (mBezierStart1.x);
  310. rightx = (int) (mBezierStart1.x + mTouchToCornerDis / 4);
  311. mBackShadowDrawable = mBackShadowDrawableLR;
  312. } else {
  313. leftx = (int) (mBezierStart1.x - mTouchToCornerDis / 4);
  314. rightx = (int) mBezierStart1.x;
  315. mBackShadowDrawable = mBackShadowDrawableRL;
  316. }
  317. canvas.save();
  318. canvas.clipPath(mPath0);
  319. canvas.clipPath(mPath1, Region.Op.INTERSECT);
  320. canvas.drawBitmap(bitmap, 0, 0, null);
  321. canvas.rotate(mDegrees, mBezierStart1.x, mBezierStart1.y);
  322. mBackShadowDrawable.setBounds(leftx, (int) mBezierStart1.y, rightx,
  323. (int) (mMaxLength + mBezierStart1.y));
  324. mBackShadowDrawable.draw(canvas);
  325. canvas.restore();
  326. }
  327. public void setBitmaps(Bitmap bm1, Bitmap bm2) {
  328. mCurPageBitmap = bm1;
  329. mNextPageBitmap = bm2;
  330. }
  331. public void setScreen(int w, int h) {
  332. mWidth = w;
  333. mHeight = h;
  334. }
  335. @Override
  336. protected void onDraw(Canvas canvas) {
  337. canvas.drawColor(0xFFAAAAAA);
  338. calcPoints();
  339. drawCurrentPageArea(canvas, mCurPageBitmap, mPath0);
  340. drawNextPageAreaAndShadow(canvas, mNextPageBitmap);
  341. drawCurrentPageShadow(canvas);
  342. drawCurrentBackArea(canvas, mCurPageBitmap);
  343. }
  344. /**
  345. * 创建阴影的GradientDrawable
  346. */
  347. private void createDrawable() {
  348. /*
  349. * GradientDrawable 支持使用渐变色来绘制图形,通常可以用作Button或是背景图形。
  350. * GradientDrawable允许指定绘制图形的种类:LINE,OVAL,RECTANGLE或是RING ,颜色渐变支持LINEAR_GRADIENT,RADIAL_GRADIENT 和 SWEEP_GRADIENT。
  351. * 其中在使用RECTANGLE(矩形),还允许设置矩形四个角为圆角,每个圆角的半径可以分别设置:
  352. * public void setCornerRadii(float[] radii)
  353. * radii 数组分别指定四个圆角的半径,每个角可以指定[X_Radius,Y_Radius],四个圆角的顺序为左上,右上,右下,左下。如果X_Radius,Y_Radius为0表示还是直角。
  354. * 颜色渐变的方向由GradientDrawable.Orientation定义,共八种
  355. * GradientDrawable的构造函数:public GradientDrawable(GradientDrawable.Orientation orientation, int[] colors)
  356. * orientation指定了渐变的方向,渐变的颜色由colors数组指定,数组中的每个值为一个颜色。
  357. * 本例定义一个渐变方向从组左上到右下,渐变颜色为红,绿,蓝三色:
  358. * mDrawable = new GradientDrawable(GradientDrawable.Orientation.TL_BR,new int[] { 0xFFFF0000, 0xFF00FF00,0xFF0000FF });
  359. * 分别使用Liner,Radial 和Sweep三种渐变模式,并可配合指定矩形四个角圆角半径
  360. * */
  361. int[] color = { 0x333333, 0x333333 };
  362. //从右向左由颜色0x333333渐变为0x333333
  363. mFolderShadowDrawableRL = new GradientDrawable(
  364. GradientDrawable.Orientation.RIGHT_LEFT, color);
  365. mFolderShadowDrawableRL
  366. .setGradientType(GradientDrawable.LINEAR_GRADIENT);//线性渐变, "radial":径向渐变,  "sweep" :角度渐变
  367. mFolderShadowDrawableLR = new GradientDrawable(
  368. GradientDrawable.Orientation.LEFT_RIGHT, color);
  369. mFolderShadowDrawableLR
  370. .setGradientType(GradientDrawable.LINEAR_GRADIENT);
  371. mBackShadowColors = new int[] { 0xff111111, 0x111111 };
  372. mBackShadowDrawableRL = new GradientDrawable(
  373. GradientDrawable.Orientation.RIGHT_LEFT, mBackShadowColors);
  374. mBackShadowDrawableRL.setGradientType(GradientDrawable.LINEAR_GRADIENT);
  375. mBackShadowDrawableLR = new GradientDrawable(
  376. GradientDrawable.Orientation.LEFT_RIGHT, mBackShadowColors);
  377. mBackShadowDrawableLR.setGradientType(GradientDrawable.LINEAR_GRADIENT);
  378. mFrontShadowColors = new int[] { 0x80111111, 0x111111 };
  379. mFrontShadowDrawableVLR = new GradientDrawable(
  380. GradientDrawable.Orientation.LEFT_RIGHT, mFrontShadowColors);
  381. mFrontShadowDrawableVLR
  382. .setGradientType(GradientDrawable.LINEAR_GRADIENT);
  383. mFrontShadowDrawableVRL = new GradientDrawable(
  384. GradientDrawable.Orientation.RIGHT_LEFT, mFrontShadowColors);
  385. mFrontShadowDrawableVRL
  386. .setGradientType(GradientDrawable.LINEAR_GRADIENT);
  387. mFrontShadowDrawableHTB = new GradientDrawable(
  388. GradientDrawable.Orientation.TOP_BOTTOM, mFrontShadowColors);
  389. mFrontShadowDrawableHTB
  390. .setGradientType(GradientDrawable.LINEAR_GRADIENT);
  391. mFrontShadowDrawableHBT = new GradientDrawable(
  392. GradientDrawable.Orientation.BOTTOM_TOP, mFrontShadowColors);
  393. mFrontShadowDrawableHBT
  394. .setGradientType(GradientDrawable.LINEAR_GRADIENT);
  395. }
  396. /**
  397. * 绘制翻起页的阴影
  398. */
  399. public void drawCurrentPageShadow(Canvas canvas) {
  400. double degree;
  401. //计算两点间连线的倾斜角.
  402. //还可旋转饼图
  403. if (mIsRTandLB) {
  404. degree = Math.PI
  405. / 4
  406. - Math.atan2(mBezierControl1.y - mTouch.y, mTouch.x
  407. - mBezierControl1.x);
  408. } else {
  409. degree = Math.PI
  410. / 4
  411. - Math.atan2(mTouch.y - mBezierControl1.y, mTouch.x
  412. - mBezierControl1.x);
  413. }
  414. // 翻起页阴影顶点与touch点的距离
  415. double d1 = (float) 25 * 1.414 * Math.cos(degree);
  416. double d2 = (float) 25 * 1.414 * Math.sin(degree);
  417. float x = (float) (mTouch.x + d1);
  418. float y;
  419. if (mIsRTandLB) {
  420. y = (float) (mTouch.y + d2);
  421. } else {
  422. y = (float) (mTouch.y - d2);
  423. }
  424. mPath1.reset();
  425. mPath1.moveTo(x, y);
  426. mPath1.lineTo(mTouch.x, mTouch.y);
  427. mPath1.lineTo(mBezierControl1.x, mBezierControl1.y);
  428. mPath1.lineTo(mBezierStart1.x, mBezierStart1.y);
  429. mPath1.close();
  430. float rotateDegrees;
  431. canvas.save();
  432. canvas.clipPath(mPath0, Region.Op.XOR);
  433. canvas.clipPath(mPath1, Region.Op.INTERSECT);
  434. int leftx;
  435. int rightx;
  436. GradientDrawable mCurrentPageShadow;
  437. if (mIsRTandLB) {
  438. leftx = (int) (mBezierControl1.x);
  439. rightx = (int) mBezierControl1.x + 25;
  440. mCurrentPageShadow = mFrontShadowDrawableVLR;
  441. } else {
  442. leftx = (int) (mBezierControl1.x - 25);
  443. rightx = (int) mBezierControl1.x + 1;
  444. mCurrentPageShadow = mFrontShadowDrawableVRL;
  445. }
  446. rotateDegrees = (float) Math.toDegrees(Math.atan2(mTouch.x
  447. - mBezierControl1.x, mBezierControl1.y - mTouch.y));
  448. canvas.rotate(rotateDegrees, mBezierControl1.x, mBezierControl1.y);
  449. mCurrentPageShadow.setBounds(leftx,
  450. (int) (mBezierControl1.y - mMaxLength), rightx,
  451. (int) (mBezierControl1.y));
  452. mCurrentPageShadow.draw(canvas);
  453. canvas.restore();
  454. mPath1.reset();
  455. mPath1.moveTo(x, y);
  456. mPath1.lineTo(mTouch.x, mTouch.y);
  457. mPath1.lineTo(mBezierControl2.x, mBezierControl2.y);
  458. mPath1.lineTo(mBezierStart2.x, mBezierStart2.y);
  459. mPath1.close();
  460. canvas.save();
  461. canvas.clipPath(mPath0, Region.Op.XOR);
  462. canvas.clipPath(mPath1, Region.Op.INTERSECT);
  463. if (mIsRTandLB) {
  464. leftx = (int) (mBezierControl2.y);
  465. rightx = (int) (mBezierControl2.y + 25);
  466. mCurrentPageShadow = mFrontShadowDrawableHTB;
  467. } else {
  468. leftx = (int) (mBezierControl2.y - 25);
  469. rightx = (int) (mBezierControl2.y + 1);
  470. mCurrentPageShadow = mFrontShadowDrawableHBT;
  471. }
  472. rotateDegrees = (float) Math.toDegrees(Math.atan2(mBezierControl2.y
  473. - mTouch.y, mBezierControl2.x - mTouch.x));
  474. canvas.rotate(rotateDegrees, mBezierControl2.x, mBezierControl2.y);
  475. float temp;
  476. if (mBezierControl2.y < 0)
  477. temp = mBezierControl2.y - mHeight;
  478. else
  479. temp = mBezierControl2.y;
  480. int hmg = (int) Math.hypot(mBezierControl2.x, temp);
  481. if (hmg > mMaxLength)
  482. mCurrentPageShadow
  483. .setBounds((int) (mBezierControl2.x - 25) - hmg, leftx,
  484. (int) (mBezierControl2.x + mMaxLength) - hmg,
  485. rightx);
  486. else
  487. mCurrentPageShadow.setBounds(
  488. (int) (mBezierControl2.x - mMaxLength), leftx,
  489. (int) (mBezierControl2.x), rightx);
  490. mCurrentPageShadow.draw(canvas);
  491. canvas.restore();
  492. }
  493. /**
  494. * 绘制翻起页背面
  495. */
  496. private void drawCurrentBackArea(Canvas canvas, Bitmap bitmap) {
  497. int i = (int) (mBezierStart1.x + mBezierControl1.x) / 2;
  498. float f1 = Math.abs(i - mBezierControl1.x);
  499. int i1 = (int) (mBezierStart2.y + mBezierControl2.y) / 2;
  500. float f2 = Math.abs(i1 - mBezierControl2.y);
  501. float f3 = Math.min(f1, f2);
  502. mPath1.reset();
  503. mPath1.moveTo(mBeziervertex2.x, mBeziervertex2.y);
  504. mPath1.lineTo(mBeziervertex1.x, mBeziervertex1.y);
  505. mPath1.lineTo(mBezierEnd1.x, mBezierEnd1.y);
  506. mPath1.lineTo(mTouch.x, mTouch.y);
  507. mPath1.lineTo(mBezierEnd2.x, mBezierEnd2.y);
  508. mPath1.close();
  509. GradientDrawable mFolderShadowDrawable;
  510. int left;
  511. int right;
  512. if (mIsRTandLB) {
  513. left = (int) (mBezierStart1.x - 1);
  514. right = (int) (mBezierStart1.x + f3 + 1);
  515. mFolderShadowDrawable = mFolderShadowDrawableLR;
  516. } else {
  517. left = (int) (mBezierStart1.x - f3 - 1);
  518. right = (int) (mBezierStart1.x + 1);
  519. mFolderShadowDrawable = mFolderShadowDrawableRL;
  520. }
  521. canvas.save();
  522. canvas.clipPath(mPath0);
  523. canvas.clipPath(mPath1, Region.Op.INTERSECT);
  524. mPaint.setColorFilter(mColorMatrixFilter);
  525. float dis = (float) Math.hypot(mCornerX - mBezierControl1.x,
  526. mBezierControl2.y - mCornerY);
  527. float f8 = (mCornerX - mBezierControl1.x) / dis;
  528. float f9 = (mBezierControl2.y - mCornerY) / dis;
  529. mMatrixArray[0] = 1 - 2 * f9 * f9;
  530. mMatrixArray[1] = 2 * f8 * f9;
  531. mMatrixArray[3] = mMatrixArray[1];
  532. mMatrixArray[4] = 1 - 2 * f8 * f8;
  533. mMatrix.reset();
  534. mMatrix.setValues(mMatrixArray);
  535. mMatrix.preTranslate(-mBezierControl1.x, -mBezierControl1.y);
  536. mMatrix.postTranslate(mBezierControl1.x, mBezierControl1.y);
  537. canvas.drawBitmap(bitmap, mMatrix, mPaint);
  538. // canvas.drawBitmap(bitmap, mMatrix, null);
  539. mPaint.setColorFilter(null);
  540. canvas.rotate(mDegrees, mBezierStart1.x, mBezierStart1.y);
  541. mFolderShadowDrawable.setBounds(left, (int) mBezierStart1.y, right,
  542. (int) (mBezierStart1.y + mMaxLength));
  543. mFolderShadowDrawable.draw(canvas);
  544. canvas.restore();
  545. }
  546. public void computeScroll() {
  547. super.computeScroll();
  548. if (mScroller.computeScrollOffset()) {
  549. float x = mScroller.getCurrX();
  550. float y = mScroller.getCurrY();
  551. mTouch.x = x;
  552. mTouch.y = y;
  553. postInvalidate();
  554. }
  555. }
  556. private void startAnimation(int delayMillis) {
  557. int dx, dy;
  558. // dx 水平方向滑动的距离,负值会使滚动向左滚动
  559. // dy 垂直方向滑动的距离,负值会使滚动向上滚动
  560. if (mCornerX > 0) {
  561. dx = -(int) (mWidth + mTouch.x);
  562. } else {
  563. dx = (int) (mWidth - mTouch.x + mWidth);
  564. }
  565. if (mCornerY > 0) {
  566. dy = (int) (mHeight - mTouch.y);
  567. } else {
  568. dy = (int) (1 - mTouch.y); // // 防止mTouch.y最终变为0
  569. }
  570. //Start scrolling by providing a starting point and the distance to travel.
  571. mScroller.startScroll((int) mTouch.x, (int) mTouch.y, dx, dy,
  572. delayMillis);
  573. }
  574. public void abortAnimation() {
  575. if (!mScroller.isFinished()) {
  576. //停止动画,与forceFinished(boolean)相反,Scroller滚动到最终x与y位置时中止动画。
  577. mScroller.abortAnimation();
  578. }
  579. }
  580. public boolean canDragOver() {
  581. //设置开始翻页的条件
  582. //      if (mTouchToCornerDis > mWidth / 10)
  583. if (mTouchToCornerDis>1)
  584. return true;
  585. return false;
  586. }
  587. /**
  588. * 是否从左边翻向右边
  589. */
  590. public boolean DragToRight() {
  591. if (mCornerX > 0)
  592. return false;
  593. return true;
  594. }
  595. }

Android电子书翻页效果实现相关推荐

  1. Android电子书翻页效果

    效果如图: 思路:计算出出各个顶点作为path路径连接 如图 设屏幕右下角为原点O (view宽,view高),e为手指触摸屏幕的坐标,我们先算出点f1与f2的坐标 此处em代表两个点的线段距离,下文 ...

  2. HTML5电子书翻页效果 代码特效+鼠标点击拖拽滑动翻页+点击书页内容放大+不支持中文

    介绍 源码名称:[HTML5电子书翻页效果]代码特效+鼠标点击拖拽滑动翻页+点击书页内容放大+不支持中文 源码大小:237KB 开发语言:PHP+Mysql 操作系统:Windows,Linux 源码 ...

  3. 获得一个不错的电子书翻页效果,和大家分享

    前些天在网上搜到一个不错的电子书的翻页效果的源码,也做了个Dome,感觉很好玩,这里来和大家一起分享下. 效果已经被写成了一个ViewController,我们只要让自己的显示界面继承这个ViewCo ...

  4. javascript移动端 电子书 翻页效果

    1.后端给一长串的纯文本 2.前端根据屏幕的高度,将文本切割为 n 页 3.使用插件 turn.js 将切割好的每页,加上翻书效果 <!DOCTYPE html> <html lan ...

  5. Next FlipBook Maker Pro(h5电子书翻页效果制作软件)官方正式版V2.7.20 | h5翻页杂志制作软件下载

    ​            Next FlipBook Maker Pro 是一款优秀实用且酷炫逼真的交互式H5翻页电子杂志制作软件,为广大用户提供了一种将静态PDF文档或图片转换为带有H5翻页效果的数 ...

  6. 在线电子书翻页效果 Turn.js

    1 html中引入 <script type="text/javascript" src="js/turn.js"></script> ...

  7. 电子书翻页效果(转)

    两个UI的view类和一个使用方法的demo. 第一个pageTurnerView.java: public class PageTurnerViewP1 extends RelativeLayout ...

  8. OpenCV+Mediapipe+UDP+Unity挥手电子书翻页

    OpenCV+Mediapipe+UDP+Unity挥手电子书翻页 效果视频如下 OpenCV+Mediapipe+UDP+Unity挥手翻页 Python端 其实很简单,基本都是现成的东西. 主要做 ...

  9. android 电子书源码,翻页效果

    这两天模仿着做了一个apk电子书的应用,有翻页效果,本来是想学一下自己写的,无奈,最后偷懒使用了别人写的 翻页类 PageWidget.java 下面是工程文件的结构 这个是写的类的包结构,PageV ...

最新文章

  1. 浅谈Disruptor
  2. 头文件的用法及注意事项
  3. 【ZZ】字符编码笔记:ASCII,Unicode和UTF-8
  4. 使用NPOI库导入导出EXCEL
  5. linux-文件与目录权限-0913
  6. 各种Python简单功能代码
  7. 搜索,推荐,广告系统架构及算法技术资料大合集吐血整理——2020年终分享
  8. 设计模式---订阅发布模式(Subscribe/Publish)
  9. CCF计算机软件能力认证试题练习:201903-2 二十四点
  10. c++求余弦的泰勒展开式
  11. Detached InstanceError:Instance is not bound to a Session 关闭session后使用SQLAlchemy对象
  12. [wp] HITB CTF 2017 website
  13. WinRAR 4.01 key!注册文件 注册码
  14. kali 更新后出现乱码的解决方案
  15. 对话窗口、面板滚动视图、标签切换视图;QDialog、QScroollArea、
  16. 三年级江苏凤凰出版社计算机,苏教版小学信息技术三年级下册教案全集
  17. matlab遗传算法配送路径,基于遗传算法的生鲜配送的路径优化问题
  18. 网页版全景图服务器搭建,云服务器全景图
  19. [ESP][驱动]ST7701S RGB屏幕驱动
  20. Linux内核版本和发行版本的区别

热门文章

  1. Jenkins的kubernetes-plugin使用方法
  2. AI会P图:你来描述,我来P
  3. 天津理工上机c语言报告5,天津理工大学C语言上机报告题目加答案.doc
  4. MoshUp安卓版来了
  5. Python pandas库|任凭弱水三千,我只取一瓢饮(6)
  6. 弱校联萌十一大决战之如日中天A Ariel【二进制+排序】
  7. 系统安全和应用及实验部分(弱口令扫描、密码破解、NMAP嗅探)
  8. OpenCV-用图像处理作出素描图(给你的另一半试试吧)
  9. linux下 18 个实用的终端命令行工具
  10. html5写手机端页面