电子书翻页功能android_android 电子书翻页 - CSDN
  • Android电子书翻页效果实现

    千次阅读 多人点赞 2013-08-04 20:37:13
    这篇文章是在参考了别人的博客基础上,修改了其中一个翻页bug,并且加了详细注释 先看效果 其中使用了贝赛尔曲线原理,关于贝赛尔曲线的知识,推荐大家看下http://blog.csdn.net/hmg25的博客 主函数 package ...

    这篇文章是在参考了别人的博客基础上,修改了其中一个翻页bug,并且加了详细注释,学习使用。

    先看效果


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

    主函数

    package com.zhang;
    
    import java.io.IOException;
    import android.app.Activity;
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.graphics.Canvas;
    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 TurnBook 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);//设置分辨率为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) {
    						//停止动画。与forceFinished(boolean)相反,Scroller滚动到最终x与y位置时中止动画。			
    						mPageWidget.abortAnimation();
    						//计算拖拽点对应的拖拽角
    						mPageWidget.calcCornerXY(e.getX(), e.getY());
                            //将文字绘于当前页
    						pagefactory.onDraw(mCurPageCanvas);
    						if (mPageWidget.DragToRight()) {
    							//是否从左边翻向右边
    							try {
    								//true,显示上一页					
    								pagefactory.prePage();
    							} catch (IOException e1) {
    								// TODO Auto-generated catch block
    								e1.printStackTrace();
    							}						
    							if(pagefactory.isfirstPage())return false;
    							pagefactory.onDraw(mNextPageCanvas);
    						} else {
    							try {
    								//false,显示下一页							
    								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;
    			}
    
    		});
    	}
    }



    package com.zhang;
    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;
    		
    		/*
    		 * 内存映射文件能让你创建和修改那些因为太大而无法放入内存的文件。有了内存映射文件,你就可以认为文件已经全部读进了内存,
    		 * 然后把它当成一个非常大的数组来访问。这种解决办法能大大简化修改文件的代码。 
    		 * 
    		* fileChannel.map(FileChannel.MapMode mode, long position, long size)将此通道的文件区域直接映射到内存中。但是,你必
    		* 须指明,它是从文件的哪个位置开始映射的,映射的范围又有多大
    		*/
    		FileChannel fc=new RandomAccessFile(book_file, "r").getChannel();
    		
    		//文件通道的可读可写要建立在文件流本身可读写的基础之上  
    		m_mbBuf =fc.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;//结束位置后移paraBuf.length
    			try {
    				strParagraph = new String(paraBuf, m_strCharsetName);//通过decode指定的编码格式将byte[]转换为字符串			
    				} 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);//截取从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();//Removes all elements from this vector, leaving it empty.
    		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;
    				//从(x,y)坐标将文字绘于手机屏幕		
    				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) + "%";
    		
    		//计算999.9%所占的像素宽度	
    		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 com.zhang;
    
    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 = "Book_Turn";
    	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:PointF holds two float coordinates
    	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
    		/**   
    	     * Paint类介绍   
    	     *    
    	     * Paint即画笔,在绘图过程中起到了极其重要的作用,画笔主要保存了颜色,   
    	     * 样式等绘制信息,指定了如何绘制文本和图形,画笔对象有很多设置方法,   
    	     * 大体上可以分为两类,一类与图形绘制相关,一类与文本绘制相关。          
    	     *    
    	     * 1.图形绘制   
    	     * setARGB(int a,int r,int g,int b);   
    	     * 设置绘制的颜色,a代表透明度,r,g,b代表颜色值。   
    	     *    
    	     * setAlpha(int a);   
    	     * 设置绘制图形的透明度。   
    	     *    
    	     * setColor(int color);   
    	     * 设置绘制的颜色,使用颜色值来表示,该颜色值包括透明度和RGB颜色。   
    	     *    
    	    * setAntiAlias(boolean aa);   
    	     * 设置是否使用抗锯齿功能,会消耗较大资源,绘制图形速度会变慢。   
    	     *    
    	     * setDither(boolean dither);   
    	     * 设定是否使用图像抖动处理,会使绘制出来的图片颜色更加平滑和饱满,图像更加清晰   
    	     *    
    	     * setFilterBitmap(boolean filter);   
    	     * 如果该项设置为true,则图像在动画进行中会滤掉对Bitmap图像的优化操作,加快显示   
    	     * 速度,本设置项依赖于dither和xfermode的设置   
    	     *    
    	     * setMaskFilter(MaskFilter maskfilter);   
    	     * 设置MaskFilter,可以用不同的MaskFilter实现滤镜的效果,如滤化,立体等       *    
    	     * setColorFilter(ColorFilter colorfilter);   
    	     * 设置颜色过滤器,可以在绘制颜色时实现不用颜色的变换效果   
    	     *    
    	     * setPathEffect(PathEffect effect);   
    	     * 设置绘制路径的效果,如点画线等   
    	     *    
    	     * setShader(Shader shader);   
    	     * 设置图像效果,使用Shader可以绘制出各种渐变效果   
    	     *   
    	     * setShadowLayer(float radius ,float dx,float dy,int color);   
    	     * 在图形下面设置阴影层,产生阴影效果,radius为阴影的角度,dx和dy为阴影在x轴和y轴上的距离,color为阴影的颜色   
    	     *    
    	     * setStyle(Paint.Style style);   
    	     * 设置画笔的样式,为FILL,FILL_OR_STROKE,或STROKE   
    	     *    
    	     * setStrokeCap(Paint.Cap cap);   
    	     * 当画笔样式为STROKE或FILL_OR_STROKE时,设置笔刷的图形样式,如圆形样式   
    	     * Cap.ROUND,或方形样式Cap.SQUARE   
    	     *    
    	     * setSrokeJoin(Paint.Join join);   
    	     * 设置绘制时各图形的结合方式,如平滑效果等   
    	     *    
    	     * setStrokeWidth(float width);   
    	     * 当画笔样式为STROKE或FILL_OR_STROKE时,设置笔刷的粗细度   
    	     *    
    	     * setXfermode(Xfermode xfermode);   
    	     * 设置图形重叠时的处理方式,如合并,取交集或并集,经常用来制作橡皮的擦除效果   
    	     *    
    	     * 2.文本绘制   
    	     * setFakeBoldText(boolean fakeBoldText);   
    	     * 模拟实现粗体文字,设置在小字体上效果会非常差   
    	     *    
    	     * setSubpixelText(boolean subpixelText);   
    	     * 设置该项为true,将有助于文本在LCD屏幕上的显示效果   
    	     *       
    	   * setTextScaleX(float scaleX);   
    	    * 设置绘制文字x轴的缩放比例,可以实现文字的拉伸的效果   
    	     *    
    	     * setTextSkewX(float skewX);   
    	     * 设置斜体文字,skewX为倾斜弧度   
    	     *    
    	     * setTypeface(Typeface typeface);   
    	     * 设置Typeface对象,即字体风格,包括粗体,斜体以及衬线体,非衬线体等   
    	     *    
    	     * setUnderlineText(boolean underlineText);   
    	     * 设置带有下划线的文字效果   
    	     *    
    	     * setStrikeThruText(boolean strikeThruText);   
    	     * 设置带有删除线的效果   
    	     *    
    	     */    
    		mPath0 = new Path();//Path路径对象 
    		mPath1 = new Path();
    		createDrawable();
    
    		mPaint = new Paint();
    		mPaint.setStyle(Paint.Style.FILL);
    
    		//颜色矩阵(ColorMatrix)和坐标变换矩阵(Matrix),对图片进行变换,以拉伸,扭曲等
    		ColorMatrix cm = new ColorMatrix();
    		
    		//颜色矩阵,颜色矩阵是一个5x4 的矩阵,可以用来方便的修改图片中RGBA各分量的值,颜色矩阵以一维数组的方式存储如下:
    		// [ a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t ],他通过RGBA四个通道来直接操作对应颜色
    		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);
    		//颜色滤镜,就像QQ的在线和离线图片,同一张图片通过颜色滤镜处理,显示不同的效果,可减少图片资源
    		mColorMatrixFilter = new ColorMatrixColorFilter(cm);
    		mMatrix = new Matrix();
    		mScroller = new Scroller(getContext());
    
    		mTouch.x = 0.01f; // 不让x,y为0,否则在点计算时会有问题
    		mTouch.y = 0.01f;
    	}
    
    	/**
    	 * 计算拖拽点对应的拖拽角
    	 */
    	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();
    			/* Android提供了Invalidate和postInvalidate方法实现界面刷新,但是Invalidate不能直接在线程中调用,因为他是违背了单线程模型:
    			 * Android UI操作并不是线程安全的,并且这些操作必须在UI线程中调用。 
    			 * invalidate()的调用是把之前的旧的view从主UI线程队列中pop掉
    			 * 而postInvalidate()在工作者线程中被调用
    			*/
    			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返回没有静止时的状态
    				mTouch.y = mCornerY - 0.09f;//- 0.09f是防止mTouch = 800 或mTouch= 0 ,在这些值时会出现BUG
    			}
    
    			this.postInvalidate();
    		}
    		// return super.onTouchEvent(event);
    		return true;
    	}
    
    	/**
    	 * 求解直线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);
    
    		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);
    	
    				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);
    
    		/*
    		 * 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);
    	}
    
    	/**
    	 * 创建阴影的GradientDrawable
    	 */
    	private void createDrawable() {
    		
    		/*
    		 * GradientDrawable 支持使用渐变色来绘制图形,通常可以用作Button或是背景图形。
    
    		* GradientDrawable允许指定绘制图形的种类:LINE,OVAL,RECTANGLE或是RING ,颜色渐变支持LINEAR_GRADIENT,RADIAL_GRADIENT 和 SWEEP_GRADIENT。
    
    		* 其中在使用RECTANGLE(矩形),还允许设置矩形四个角为圆角,每个圆角的半径可以分别设置:
    
    		* public void setCornerRadii(float[] radii)
    
    		* radii 数组分别指定四个圆角的半径,每个角可以指定[X_Radius,Y_Radius],四个圆角的顺序为左上,右上,右下,左下。如果X_Radius,Y_Radius为0表示还是直角。
    
    		* 颜色渐变的方向由GradientDrawable.Orientation定义,共八种
    		
    		* GradientDrawable的构造函数:public GradientDrawable(GradientDrawable.Orientation orientation, int[] colors)
    
    		* orientation指定了渐变的方向,渐变的颜色由colors数组指定,数组中的每个值为一个颜色。
    
    		* 本例定义一个渐变方向从组左上到右下,渐变颜色为红,绿,蓝三色:
    		
    		* mDrawable = new GradientDrawable(GradientDrawable.Orientation.TL_BR,new int[] { 0xFFFF0000, 0xFF00FF00,0xFF0000FF });
    		
    		* 分别使用Liner,Radial 和Sweep三种渐变模式,并可配合指定矩形四个角圆角半径
    		* */
    		
    		int[] color = { 0x333333, 0x333333 };
    		
    		//从右向左由颜色0x333333渐变为0x333333
    		mFolderShadowDrawableRL = new GradientDrawable(
    				GradientDrawable.Orientation.RIGHT_LEFT, color);
    		mFolderShadowDrawableRL
    				.setGradientType(GradientDrawable.LINEAR_GRADIENT);//线性渐变, "radial":径向渐变,  "sweep" :角度渐变
    		
    		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);
    	}
    
    	/**
    	 * 绘制翻起页的阴影
    	 */
    	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);
    
    		mCurrentPageShadow.draw(canvas);
    		canvas.restore();
    	}
    
    	/**
    	 * 绘制翻起页背面
    	 */
    	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
    		}
    		//Start scrolling by providing a starting point and the distance to travel.
    		mScroller.startScroll((int) mTouch.x, (int) mTouch.y, dx, dy,
    				delayMillis);
    	}
    
    	public void abortAnimation() {
    		if (!mScroller.isFinished()) {
    			//停止动画,与forceFinished(boolean)相反,Scroller滚动到最终x与y位置时中止动画。
    			mScroller.abortAnimation();
    		}
    	}
    
    	public boolean canDragOver() {
    		//设置开始翻页的条件
    //		if (mTouchToCornerDis > mWidth / 10)
    			if (mTouchToCornerDis>1)
    			return true;
    		return false;
    	}
    
    	/**
    	 * 是否从左边翻向右边
    	 */
    	public boolean DragToRight() {
    		if (mCornerX > 0)
    			return false;
    		return true;
    	}
    
    }
    

    THE END

    展开全文
  • Android 实现书籍翻页效果----完结篇

    万次阅读 热门讨论 2011-05-25 08:38:00
    By 何明桂(http://blog.csdn.net/hmg25) 转载...需要转向去研究framework层(短暂的酱油期就这样结束啦 o(︶︿︶)o唉),将会暂停翻页的开发,所以想要进一步提高功能的童鞋需要自己动手~~~稍后发布的将是本人提供的

                                                   By 何明桂(http://blog.csdn.net/hmg25) 转载请注明出处

          之前由于种种琐事,暂停了这个翻页效果的实现,终于在这周末完成了大部分功能,但是这里只是给出了一个基本的雏形,没有添加翻页的动画效果,由于下个周末开始,需要转向去研究framework层(短暂的酱油期就这样结束啦 o(︶︿︶)o唉),将会暂停翻页的开发,所以想要进一步提高功能的童鞋需要自己动手~~~稍后发布的将是本人提供的完结篇代码。  

        今天一个热心的csdn好友-- xiaofanqingzjj 告诉我:“这两天把你的代码整了一下,实现了 根据滑动速度或位置翻页自动彈回,或者自动翻转到下一页的动画,等整好了,再发布上来”, 呵呵,感想他的热心,也希望以后大家有什么好的改进也可以发布出来让大家都可以一起学习下。

       闲话少说,在最后关头和大家说说完结篇代码里的改进 ,上图看效果:

       

     

                                           By 何明桂(http://blog.csdn.net/hmg25) 转载请注明出处

        有图可以看到,首先是修复了之前翻起页阴影顶点,定位异常的问题,然后是添加了翻起页背面的显示,以及光影效果,并且修复了,放翻页趋向于垂直方向时,光影效果出现的漂移现象。

        文章后边已经上传翻页效果的源码了,我这里不详细讲太多,稍后有时间的话,我会把光影效果这部分代码的原理,另外写一篇博客。下面只是给个概述,方便大家研究代码。

       首先分析阴影顶点的定位问题,先来看一种特殊情况:

     

          假设直线aT处于垂直位置,两边阴影宽度都为一致,假设为25px, 容易得aT为25*√2=25*1.414,那么处于这种特殊情况下的顶点为:

      a.x=T.x;

     a.y=T.y-25*1.414

     

    现在我们来看一般性情况:

     

    AT依旧为25*1.414,那么如果要定位A点的坐标,就需要求出AB和BT的长度(AB垂直于BT),通过分析可以知道夹角BAT,等于45度角加上夹脚DTE,而夹脚DTE是可以通过Touch点和mBezierControl1的坐标求出的:

     

        Math.atan2(mBezierControl1.y - mTouch.y, mTouch.x- mBezierControl1.x);

     

     

       通过以上计算就可以求出阴影顶点坐标了。

     

    翻起页背面分为两部分求解,第一部分是将原图翻转得到:

     

    以上效果是通过创建一个Matrix mMatrix和float[] mMatrixArray 实现

     

     

    mMatrix.setValues(mMatrixArray);

    mMatrix.preTranslate(-mBezierControl1.x, -mBezierControl1.y);

    mMatrix.postTranslate(mBezierControl1.x, mBezierControl1.y);

     

     

    翻转之后为了实现翻起后的光影效果,需要使用 ColorMatrixFilter ,实现以下效果,对这两个不熟的自己找资料研究去~~~╭(╯^╰)╮

     

     

    呵呵,大概就是这些个内容了,具体的自己研究代码去~~下边给出一个程序中各个点的标示,方便研究:

     

     

     

                         By 何明桂(http://blog.csdn.net/hmg25) 转载请注明出处

     

    源码地址:http://download.csdn.net/source/3216809

     

     

    希望大家也把自己改动的地方发布出来一起研究。

     

    PS:我新写了一篇博客,在博客中对原来地翻页进行了升级,添加了翻页动画效果,并且新添加了一个类,用于读取SD卡中对txt

    文本,实现了一个简易的电子书阅读器。请有兴趣对童鞋,移步至:http://blog.csdn.net/hmg25/archive/2011/05/14/6419694.aspx

     

     

     

     

    展开全文
  • Android 实现书籍翻页效果----升级篇

    万次阅读 热门讨论 2011-05-14 14:42:00
     自从之前发布了《Android 实现书籍翻页效果----完结篇 》之后,收到了很多朋友给我留言,前段时间由于事情较多,博客写得太匆忙很多细节地方没有描述清楚。所以不少人对其中的地方有不少不明白之处,也有...

          自从之前发布了《Android 实现书籍翻页效果----完结篇 》之后,收到了很多朋友给我留言,前段时间由于事情较多,博客写得太匆忙很多细节地方没有描述清楚。所以不少人对其中的地方有不少不明白之处,也有不少人对其中出现的Bug进行了反馈。今天终于找出了段时间对这段时间的一些问题做个简单的总结。

         之前给出的例子只是能使书籍进行简单的拖拽,没有实现翻页的动画效果,很多人希望我能加上这一个,所以首先我们就来说说这个翻页的动画。

      其实翻页的动画很容易实现,只要在Touch抬起后不断的刷新mTouch.x , mTouch.y 的值就行了,   你可以使用handler,thread,也可以使用Scroller,我个人比较喜欢Scroller,这个比较简单。

      新添两个函数:

    接着在按下抬起时调用就行了

    if (event.getAction() == MotionEvent.ACTION_UP) {
       if (canDragOver()) {   //判断是否可以翻页
        startAnimation(1200);
       } else {
        mTouch.x = mCornerX - 0.09f;   //如果不能翻页就让mTouch返回没有静止时的状态
        mTouch.y = mCornerY - 0.09f;   // - 0.09f是防止mTouch = 800 或mTouch= 0 要不在这些值时会出现BUG
       }

     

    还需要修改的地方是calcPoints() 这个函数,之前为了防止一个bug出现,添加了if (mBezierStart1.x < 0 || mBezierStart1.x > mWidth) {这个判断,但是在翻页动画时mTouch.x会小于0(从右向左翻时)或者mTouch.x>mWidth(从左往右)这时并不需要在进入这个函数进行处理,所以要在这个情况时将其屏蔽,改为:

    if (mTouch.x > 0 && mTouch.x < mWidth) {
       if (mBezierStart1.x < 0 || mBezierStart1.x > mWidth) {

    ……}

    }

    经过上边的修改就可以完成动画效果了。

     

    还有的童鞋想将这个做成一个电子书阅读器,但是不知道如何将txt中的内容转换为翻页所需的图片,并在翻页后进行切换。所以我新添加了一个简单的类BookPageFactory,用来读取SD卡中的一个txt,并将读取的内容转换为一个bitmap用于显示。哈哈,这个只是一个功能很小的类,只是给大家做个演示,起到抛砖引玉的作用。大家请根据自己所需的功能酌情修改。

    源码附带的是一个简单的带翻页动画的电子书阅读器,大家测试时请将test.txt放于SD卡根目录下:

    pagefactory.openbook("/sdcard/test.txt");

     

    新的界面截图:

           

     

     

    源码下载地址:

          http://download.csdn.net/source/3278901

     

     

     

     

     

     

    展开全文
  • Android平台上的电子书翻页效果,翻页效果非常好,有图参考,注释易懂。 已实现:实现了翻书效果,可点击翻页或拖动翻页
  • Android电子书翻页效果

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

    效果如图:
    在这里插入图片描述
    思路:计算出出各个顶点作为path路径连接 如图
    在这里插入图片描述
    在这里插入图片描述

    设屏幕右下角为原点O (view宽,view高),e为手指触摸屏幕的坐标,我们先算出点f1与f2的坐标

    在这里插入图片描述
    此处em代表两个点的线段距离,下文同上!
    em=O.y-e.y(屏幕高度-e的Y坐标) ,Om=O.x-e.x; (.x代表某个点的x坐标)
    可知方程 ef1²-f1m²=em²(勾股定理) ef1-f1m=Om; 因为em=Of1
    =>(ef1+f1m)(ef1-f1m)=em²
    =>(2
    ef1-Om)* Om=em²
    =>ef1=(em²/Om+Om)/2;
    =>f1.x=O.x-ef1

    //pointF e 求f1坐标
    float Om=getMeasuredWidth()-em.x;
    float em=getMeasuredHeight()-em.y;
    float ef1=(em*em/Om+Om)/2;
    f1.x=getMeasuredWidth()-ef1;
    f1.y=getMeasuredHeight();
    

    根据方程可计算出 f1坐标 ,同理计算出f2坐标 。 注: (应该有更简单的计算公式,不过我高中和大学数学都忘了个差不多了。。。总之先计算f1,f2坐标);

    =======================================================
    接下来计算a1和a2
    我们这里让a1.x=f1.x-(O.x-f1.x)/2
    float a1x=f1.x-(O.x-f1.x)/2;
    PointF a1=new PointF(a1x,view高)
    同理得a2

    ===========================================
    接下来计算c1 和 c2
    c1 实际上是 直线a1–a2 与 直线e–f1的交点:
    通过直线函数求两条线的交点 如下:

     public PointF getIntersection(PointF pointF1,PointF pointF2, PointF pointF3,PointF pointF4){
            //第一条直线
            float x1 = pointF1.x, y1 = pointF1.y, x2 = pointF2.x, y2 =     				pointF2.y;
            float a = (y1 - y2) / (x1 - x2);
            float b = (x1 * y2 - x2 * y1) / (x1 - x2);
           
    
            //第二条直线
            float x3 = pointF3.x, y3 = pointF3.y, x4 = pointF4.x, y4 = pointF4.y;
            float c = (y3 - y4) / (x3 - x4);
            float d = (x3 * y4 - x4 * y3) / (x3 - x4);
     
    
            float x = ((x1 - x2) * (x3 * y4 - x4 * y3) - (x3 - x4) * (x1 * y2 - x2 * y1))
                    / ((x3 - x4) * (y1 - y2) - (x1 - x2) * (y3 - y4));
    
            double y = ((y1 - y2) * (x3 * y4 - x4 * y3) - (x1 * y2 - x2 * y1) * (y3 - y4))
                    / ((y1 - y2) * (x3 - x4) - (x1 - x2) * (y3 - y4));
    
            PointF pointF = new PointF((int)x, (int)y);
            return pointF;
        }
    

    接下来求b1和b2
    b1 在 三角形a1–c1–f1 中
    zxp1.x=((zp1.x+jiao1.x)/2+p2.x)/2;

    PointF b1=new PointF();
    b1.x=((a1.x+c1.x)/2+f1.x)/2;
    b1.y=((a1.y+c1.y)/2+f1.y)/2;
    //同理的b2
    

    用Path对象将已求得点连接效果如下:
    在这里插入图片描述
    填充效果:
    在这里插入图片描述
    看起来别扭是因为在连接 c1–b1 以及 b1–a1时 用lineTo画直线函数连接,这里需要用贝塞尔曲线 path.quadTo();
    而path.quadTo();函数需要确定偏移的点
    接下来计算偏移点d1 d2 g1 g2
    在这里插入图片描述
    d1和d2很好求,就是直线ef1、ef2 与 直线b1b2的交点,通过上面的函数可得

    d1=getIntersection(b1,b2,e,f1);
    d2=getIntersection(b1,b2,e,f2);
    

    g1的y坐标为view高度,而且在直线 b1–b2上

    //直线函数为 Y=kx+b;计算k ,b
       public void getlineFun(PointF pointF1,PointF pointF2){//直线函数 确定一条直线函数
            //y=kx+b; x=(y-b)/k
            float x1=pointF1.x,y1=pointF1.y,x2=pointF2.x,y2=pointF2.y;
            float k=(y2-y1)/(x2-x1); //k
            float b=y1-(y2-y1)/(x2-x1)*x1;//b
    
            g1.y=getMeasuredHeight();
            g1.x=(g1.y-b)/k;
            g2.x=getMeasuredWidth();
            g2.y=k*g2.x+b;
        }
    
    //得出g1,g2
    getlineFun(b1,b2)
    

    最终,在连接c1–b1向b1偏移 ,b1–a1 向g1偏移
    黄色区域最终代码:

    
    path.moveTo(e.x,e.y);
    path.lineTo(c1.x,c1.y);
    
    path.quadTo(d1.x,d1.y,b1.x,b1.y);//向点d1处偏移
    path.quadTo(g1.x,g1.y,a1.x,a1.y);//向点g1偏移
    path.quadTo(g1.x,g1.y,b1.x,b1.y);//回到b1点
    
    path.lineTo(b2.x,b2.y);
    path.quadTo(g2.x,g2.y,a1.x,a1.y);
    path.quadTo(g2.x,g2.y,b2.x,b2.y);
    path.quadTo(d2.x,d2.y,c2.x,c2.y);
    path.quadTo(e.x,e.y);//回到原点
    
    

    最后用 用path连接蓝色区域 用 canvas.clipPath(path);剪裁掉画布即可完成!

    =======================================================
    结语:本人第一次写博客,如有

    展开全文
  • android 翻页卷曲效果 电子书翻页

    千次阅读 2015-03-03 18:16:51
    先上个效果图:   ...效果还是很不错的,不过与ibook那个效果比起来,还是有差距的。应为这个没用到openGL做3D效果,只是用的2d的canvas画布去画的view,添加了阴影效果,还是挺有立体感的。...
  • //读取文件,设置翻页时的阴影和背面文字 public class BookPageFactory { private File book_file = null; private MappedByteBuffer m_mbBuf = null; private int m_mbBufLen = 0; private int m_mbBufBegin =...
  • 借鉴于github上的一些功能,借鉴下来然后自己创建来用
  • 实现了网络图片仿电子书翻页效果,具体请看博客http://blog.csdn.net/crackgmkey/article/details/78220624
  • Android 电子书应用完全开源代码

    万次阅读 多人点赞 2018-10-09 14:07:42
    一款界面简洁、风格清新、功能丰富的Android电子书应用:  具有仿真翻页、覆盖翻页、直接翻页三种翻页风格;  常规、复古、护眼、夜间四种主题,可调节亮度,能根据光线自动切换夜间模式;  支持文字大小、字体...
  • android 电子书源码,翻页效果

    千次阅读 2014-02-21 16:55:47
    这两天模仿着做了一个apk电子书的应用,有翻页效果,本来是想学一下自己写的,无奈,最后偷懒使用了别人写的 翻页类 PageWidget.java 下面是工程文件的结构 这个是写的类的包结构,PageView.java 类本类是打算...
  • 之前给大家讲解了android实现书籍翻页效果的原理,并在文章结尾处说明要发布源码,呵呵,但是最近有不少琐事缠身,原计划给大家的源码demo没有时间完成,可能要delay啦~~但是由于源码实现啦原理篇所说的大部分效果,...
  • 自定义ui布局翻页地址:https://download.csdn.net/download/u011586504/10780415 图片布局翻页地址:https://download.csdn.net/download/u011586504/10780302 翻页源码地址:...
  • 亲 谁知道有电子书翻页效果可运行的Demo,或github开源项目,谢谢。。。。
  • Android ListView 滚动翻页效果

    千次阅读 2013-07-30 16:21:55
    前面说过了ListView的下拉刷新效果,有些时候我们需要让ListView在滑动到最底部时,自动去向服务器请求下一页数据。这时候就需要监听的ListView的滑动状态了。 mListView.setOnScrollListener(OnScrollListener l);...
  • Android翻页效果原理实现之翻页的尝试

    万次阅读 多人点赞 2015-01-13 17:04:15
    在《自定义控件其实很简单》系列的前半部分中我们用了整整六节近两万字两百多张配图讲了Android图形的绘制,虽然篇幅很巨大但仍然只是图形绘制的冰山一角,旨在领大家入门,至于修行成果就看各位的了……那么这个些...
  • 近期公司有一个电子书需求的开发,功能除了电子书的基本功能之外,还有长按选中,可以滑动高亮显示等等。最初是准备使用FBReader,但是发现不太优化,之前用过FBReader。然后就网上找demo,发现对于选中高亮显示真的...
  • Android浏览书籍的层叠翻页动画效果。用于演示手机阅读电子书的平滑翻页,另外说明了PDF文件渲染器PdfRenderer和栈视图StackView的使用方法。
  • android 电子书翻页效果

    千次阅读 2013-05-27 10:16:46
    import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.ColorMatrix; import android.graphics.ColorMatrixColorFilter; impor
1 2 3 4 5 ... 20
收藏数 703
精华内容 281
关键字:

电子书翻页功能android