Android翻页效果实现技术分享
android中图片翻页效果简单的实现方法

android中图⽚翻页效果简单的实现⽅法复制代码代码如下:public class PageWidget extends View {private Bitmap foreImage;private Bitmap bgImage;private PointF touchPt;private int screenWidth;private int screenHeight;private GradientDrawable shadowDrawableRL;private GradientDrawable shadowDrawableLR;private ColorMatrixColorFilter mColorMatrixFilter;private Scroller mScroller;private int lastTouchX;public PageWidget(Context context) {super(context);// TODO Auto-generated constructor stubtouchPt = new PointF(-1,-1);//ARGB A(0-透明,255-不透明)int[] color = { 0xb0333333 ,0x00333333};shadowDrawableRL = new GradientDrawable(GradientDrawable.Orientation.RIGHT_LEFT, color); shadowDrawableRL.setGradientType(GradientDrawable.LINEAR_GRADIENT);shadowDrawableLR = new GradientDrawable(GradientDrawable.Orientation.LEFT_RIGHT, color); shadowDrawableLR.setGradientType(GradientDrawable.LINEAR_GRADIENT);float array[] = { 0.55f, 0, 0, 0, 80.0f,,0.55f, 0, 0, 80.0f,, 0,0.55f, 0, 80.0f,, 0, 0, 0.2f, 0};ColorMatrix cm = new ColorMatrix();cm.set(array);/** |A*0.55 + 80|* |R*0.55 + 80|* |G*0.55 + 80|* |B*0.2|*/// cm.setSaturation(0);mColorMatrixFilter = new ColorMatrixColorFilter(cm);//利⽤滚动条来实现接触点放开后的动画效果mScroller = new Scroller(context);}@Overridepublic void computeScroll() {// TODO Auto-generated method stubif (puteScrollOffset()) {touchPt.x = mScroller.getCurrX();touchPt.y = mScroller.getCurrY();postInvalidate();}else{// touchPt.x = -1;// touchPt.y = -1;}puteScroll();}public void SetScreen(int screenWidth,int screenHeight){ this.screenWidth = screenWidth;this.screenHeight = screenHeight;}public Bitmap getForeImage() {return foreImage;}public void setForeImage(Bitmap foreImage) {this.foreImage = foreImage;}public Bitmap getBgImage() {return bgImage;}public void setBgImage(Bitmap bgImage) {this.bgImage = bgImage;}@Overrideprotected void onDraw(Canvas canvas) {// TODO Auto-generated method stubdrawPageEffect(canvas);super.onDraw(canvas);}/*** 画前景图⽚* @param canvas*/private void drawForceImage(Canvas canvas) {// TODO Auto-generated method stubPaint mPaint = new Paint();if (foreImage!=null) {canvas.drawBitmap(foreImage, 0, 0, mPaint);}}/*** 画背景图⽚* @param canvas*/private void drawBgImage(Canvas canvas,Path path) { // TODO Auto-generated method stubPaint mPaint = new Paint();if (bgImage!=null) {canvas.save();//只在与路径相交处画图canvas.clipPath(path,Op.INTERSECT);canvas.drawBitmap(bgImage, 0, 0, mPaint);canvas.restore();}}/*** 画翻页效果* @param canvas*/private void drawPageEffect(Canvas canvas) {// TODO Auto-generated method stubdrawForceImage(canvas);Paint mPaint = new Paint();if (touchPt.x!=-1 && touchPt.y!=-1) {//翻页左侧书边canvas.drawLine(touchPt.x, 0, touchPt.x,screenHeight, mPaint);//左侧书边画阴影shadowDrawableRL.setBounds((int)touchPt.x - 20, 0 ,(int)touchPt.x, screenHeight); shadowDrawableRL.draw(canvas);//翻页对折处float halfCut = touchPt.x + (screenWidth - touchPt.x)/2;canvas.drawLine(halfCut, 0, halfCut, screenHeight, mPaint);//对折处左侧画翻页页图⽚背⾯Rect backArea = new Rect((int)touchPt.x,0,(int)halfCut,screenHeight);Paint backPaint = new Paint();backPaint.setColor(0xffdacab0);canvas.drawRect(backArea, backPaint);//将翻页图⽚正⾯进⾏处理⽔平翻转并平移到touchPt.x点Paint fbPaint = new Paint();fbPaint.setColorFilter(mColorMatrixFilter);Matrix matrix = new Matrix();matrix.preScale(-1,1);matrix.postTranslate(foreImage.getWidth() + touchPt.x,0);canvas.save();canvas.clipRect(backArea);canvas.drawBitmap(foreImage, matrix, fbPaint);canvas.restore();//对折处画左侧阴影shadowDrawableRL.setBounds((int)halfCut - 50, 0 ,(int)halfCut, screenHeight);shadowDrawableRL.draw(canvas);Path bgPath = new Path();//可以显⽰背景图的区域bgPath.addRect(new RectF(halfCut,0,screenWidth,screenHeight), Direction.CW); //对折出右侧画背景drawBgImage(canvas,bgPath);//对折处画右侧阴影shadowDrawableLR.setBounds((int)halfCut, 0 ,(int)halfCut + 50, screenHeight);shadowDrawableLR.draw(canvas);}}@Overridepublic boolean onTouchEvent(MotionEvent event) {// TODO Auto-generated method stubif (event.getAction() == MotionEvent.ACTION_DOWN) {touchPt.x = event.getX();touchPt.y = event.getY();}else if(event.getAction() == MotionEvent.ACTION_MOVE){lastTouchX = (int)touchPt.x;touchPt.x = event.getX();touchPt.y = event.getY();postInvalidate();}else if(event.getAction() == MotionEvent.ACTION_UP){int dx,dy;dy = 0;//向右滑动if (lastTouchX<touchPt.x) {dx = foreImage.getWidth() - (int)touchPt.x + 30;}else{//向左滑动dx = -(int)touchPt.x - foreImage.getWidth();}mScroller.startScroll((int)touchPt.x,(int)touchPt.y,dx,dy,1000);postInvalidate();}//必须为true,否则⽆法获取ACTION_MOVE及ACTION_UP事件return true;}}public class PageActivity extends Activity {protected void onCreate(android.os.Bundle savedInstanceState) {requestWindowFeature(Window.FEATURE_NO_TITLE);PageWidget pageWidget = new PageWidget(this);Display display = getWindowManager().getDefaultDisplay();int width = display.getWidth();int height = display.getHeight();pageWidget.SetScreen(width, height);Bitmap bm1 = BitmapFactory.decodeResource(getResources(), R.drawable.pre7); Bitmap bm2 = BitmapFactory.decodeResource(getResources(), R.drawable.after7); Bitmap foreImage = Bitmap.createScaledBitmap(bm1, width, height,false);Bitmap bgImage = Bitmap.createScaledBitmap(bm2, width, height,false);pageWidget.setBgImage(bgImage);pageWidget.setForeImage(foreImage);setContentView(pageWidget);super.onCreate(savedInstanceState);};}。
Android翻页效果原理实现之引入折线

Android翻页效果原理实现之引入折线先以折页的方式对翻页过程进行一个细致的分析,然后再在下一节将折线变为曲线。
折页的实现可分为两种方式,一种是纯计算,我们利用已知的条件根据各类公式定理计算出未知的值,第二种呢则是通过图形的组合巧妙地去获取图形的交并集来实现,第二种方式需要很好的空间想象力这里就先不说了,而第一种纯计算的方式呢又可以分为使用高等数学和解三角形两种方法,前者对于数学不好的童鞋来说不易理解,这里我们选择后者使用解三角形来计算,首先我们先来搞个简单的辅助图:图很简单,一看就懂,大家可以拿个本子或者书尝试折页,不管你如何折,折叠区域AOB 和下一页显示的区域APB必定是完全相等的对吧,那么我们就可以得到一个惊人的事实:角AOB恒为直角,这时我们来添加一些辅助线便于理解:我们设折叠后的三角形AOB的短边长度为x而长边长度为y,由图可以得出以下运算:我们可以使用相同的方法去解得y的值,这里我使用的是等面积法,由图可知梯形MOBP 的面积是三角形MOA、AOB、APB面积之和:我们可以使用相同的方法去解得y的值,这里我使用的是等面积法,由图可知梯形MOBP 的面积是三角形MOA、AOB、APB面积之和:这样我们可以根据任意一点得出两边边长,我们来代码中实践一下看看是不是这样的呢?为了便于理解,这里我重新使用了一个新的FoldView:[java] view plain copy print?public class FoldView extends View {public FoldView(Context context, AttributeSet attrs) {super(context, attrs);}}那么尝试根据我们以上分析的原理来绘制这么一个折页的效果,获取事件点、获取控件宽高就不说了,我们重点来看看onDraw中的计算:[java] view plain copy print?@Overrideprotected void onDraw(Canvas canvas) {// 重绘时重置路径mPath.reset();// 绘制底色canvas.drawColor(Color.WHITE);/** 如果坐标点在右下角则不执行绘制*/if (pointX == 0 && pointY == 0) {return;}/** 额,这个该怎么注释好呢……根据图来*/float mK = mViewWidth - pointX;float mL = mViewHeight - pointY;// 需要重复使用的参数存值避免重复计算float temp = (float) (Math.pow(mL, 2) + Math.pow(mK, 2));/** 计算短边长边长度*/float sizeShort = temp / (2F * mK);float sizeLong = temp / (2F * mL);/** 生成路径*/mPath.moveTo(pointX, pointY);mPath.lineTo(mViewWidth, mViewHeight - sizeLong);mPath.lineTo(mViewWidth - sizeShort, mViewHeight);mPath.close();// 绘制路径canvas.drawPath(mPath, mPaint);}每次绘制的时候我们需要重置Path不然上一次的Path就会跟这一次叠加在一起,效果如下:(PS:动态效果图Microsoft Word 2003不好上传只有截图)效果是大致出来了,但是我们发现有一处不对的地方,当我们非常靠左或非常靠下地折叠时:如果再往左折此时我们的Path就会消失掉,其实这跟我们我们现实中的折页是一样的,折页的过程是有限制的,如下图:右下角点P因为受装订线的制约,其半径最大只能为纸张的宽度,如果我们始终以该宽度为半径折页,那么点P的轨迹就可以形成曲线Q,图中半透明红色区域为一个半圆形,也就是说,我们的点P只能在该范围内才应当有效对吧,那么该如何做限制呢?很简单,我们只需在计算长短边长之前判断触摸点是否在该区域即可:[java] view plain copy print?/*** 计算短边的有效区域*/private void computeShortSizeRegion() {// 短边圆形路径对象Path pathShortSize = new Path();// 用来装载Path边界值的RectF对象RectF rectShortSize = new RectF();// 添加圆形到PathpathShortSize.addCircle(0, mViewHeight, mViewWidth, W);// 计算边界puteBounds(rectShortSize, true);// 将Path转化为RegionmRegionShortSize.setPath(pathShortSize, new Region((int) rectShortSize.left, (int) rectShortSize.top, (int) rectShortSize.right, (int) rectShortSize.bottom));}同样计算有效区域这个过程是在onSizeChanged中进行,我们说过尽量不要在一些重复调用的方法内执行没必要的计算,在onDraw里我们只需在绘制钱判断下当前触摸点是否在该区域内,如果不在,那么我们通过坐标x轴重新计算坐标y轴:[java] view plain copy print?/** 判断触摸点是否在短边的有效区域内*/if (!mRegionShortSize.contains((int) mPointX, (int) mPointY)) {// 如果不在则通过x坐标强行重算y坐标mPointY = (float) (Math.sqrt((Math.pow(mViewWidth, 2) - Math.pow(mPointX, 2))) - mViewHeight);// 精度附加值避免精度损失mPointY = Math.abs(mPointY) + mValueAdded;}那么如何来计算y坐标呢?很简单,我们只需根据圆的方程求解即可,因为P的轨迹是个圆~在得到新的y坐标mPointY后我们还应该为其加上一点点的精度值mValueAdded来挽回因浮点计算而损失的精度。
Android中实现滑动翻页—使用ViewFlipper

ViewFilpper类继承于ViewAnimator类。而ViewAnimator类继承于FrameLayout。
查看ViewAnimator类的源码可以看出此类的作用主要是为其中的View切换提供动画效果。该类有如下几个和动画相关的方法。
setInAnimation:设置View进入屏幕时候使用的动画。该方法有两个重载方法,即可以直接传入Animation对象,也可以传入定义的Animation文件的resourceID。
setOutAnimation:设置View退出屏幕时候使用的动画。使用方法和setInAnimation方法一样。
showNext:调用该方法可以显示FrameLayout里面的下一个View。
showPrevious:调用该方法可以来显示FrameLayout里面的上一个View。
查看ViewFlipper的源码可以看到,ViewFlipper主要用来实现View的自动切换。该类提供了如下几个主要的方法。
setFilpInterval:设置View切换的时间间隔。参数为毫秒。
startFlipping:开始进行View的切换,时间间隔是上述方法设置的间隔数。切换会循环进行。
stopFlipping:停止View切换。
setAutoStart:设置是否自动开始。如果设置为“true”,当ViewFlipper显示的时候View的切换会自动开始。
一般情况下,我们都会使用ViewFilpper类实现View的切换,而不使用它的父类ViewAnimator类。
Android 翻书效果该控件大致实现方法:eBook继

Android 翻书效果该控件大致实现方法:eBook继FrameLayout,好处在于FrameLayout有图层效果,后添加的View类能覆盖前面的View。
初始化:定义一个LinearLayout的成员变量mView,将page.xml inflate 成View分别用l eftPage,rightPage引用,并初始化其数据,将l eftPage,rightPage通过ad dView添加到mView,然后将mView添加到eBook。
在eBook里定义一个私有类BookView extends View。
并定义成员变量BookView mBookView;最后将mBookView添加的eBook中,这样,mView中的内容为书面内容,mBookView中的内容为特效内容。
后续手势动作:可将各种手势的特效动作画于mBookView的画布中。
Java代码:我们在来看看xml代码:java代码:main.xml文件:<?xml version="1.0" encoding="utf-8"?>page.xml文件:<?xml version="1.0" encoding="utf-8"?>控件TelEdit.java代码:java代码:package eoe.book;import android.content.Context;import android.graphics.Canvas;import android.graphics.Col or;import android.graphics.Paint;import android.util.AttributeSet;import android.view.Wind owManager;import android.widget.EditText;public class TelEdit extends EditText {Context mContext;public TelEdit(Context context) {super(context);mContext = context;}public TelEdit(Context context, AttributeSet attrs) {super(context, attrs);mContext = context;}public TelEdit(Context context, AttributeSet attrs, int defStyl e) {super(context, attrs, defStyl e);mContext = context;}protected void onDraw(Canvas canvas) {WindowManager wm = (WindowManager) mContext.getSystemService("window");int windowWidth = wm.getDefaultDisplay().getWidth();int windowHeight = wm.getDefaultDisplay().getHeight();Paint paint = new Paint();paint.setStyl e(Paint.Styl e.FILL);paint.setCol or(Col or.BLACK);int pad dingTop = getPad dingTop();int pad dingBottom = getPad dingBottom();int scrollY = getScrollY();int scrollX = getScrollX() + wind owWidth;int innerHeight = scrollY + getHeight() - pad dingBottom;int lineHeight = getLineHeight();int baseLine = scrollY+ (lineHeight - ((scrollY - pad dingTop) % lineHeight)); int x = 8;whil e (baseLine < innerHeight) {canvas.drawLine(x, baseLine, scrollX - x, baseLine, paint);baseLine += lineHeight;}super.onDraw(canvas);}}eBook.java文件部分代码:java代码:package eoe.book;import java.util.ArrayList;import java.util.Date;import java.util.List;import android.content.Context;import android.graphics.Bitmap;import android.graphics.Canvas;import android.graphics.Col or;import android.graphics.LinearGradient;import android.graphics.Paint;import android.graphics.Path;import android.graphics.Point;import android.graphics.PorterDuffXfermod e;import android.graphics.Shader;import android.graphics.PorterDuff.Mode;import android.util.AttributeSet;import android.util.Log;import android.view.GestureDetector;import youtInflater;import android.view.MotionEvent;import android.view.View;import android.view.GestureDetector.OnGestureListener;import android.widget.FrameLayout;import android.widget.LinearLayout;public class eBook extends FrameLayout{public static final String LOG_TAG = "eBook";List myRecPages;int totalPageNum;Context mContext;bool ean hasInit = false;final int defaultWidth = 600 , defaultHeight = 400;int contentWidth = 0;int contentHeight = 0;View l eftPage,rightPage,llPage,lrPage,rrPage,rlPage;LinearLayout mView;bookView mBookView;bool ean cl oseBook = false;private enum Corner {LeftTop,RightTop,LeftBottom,RightBottom,None};private Corner mSel ectCorner;final int clickCornerLen = 250*250; //50dipfl oat scrollX = 0,scrollY = 0;int indexPage = 0;private enum State {ABOUT_TO_ANIMATE,ANIMATING,ANIMATE_END,READY,TRACKING};private State mState;private Point aniStartPos;private Point aniStopPos;private Date aniStartTime;private l ong aniTime = 2000;private l ong timeOffset = 900;Listener mListener;private GestureDetector mGestureDetector;private BookOnGestureListener mGestureListener;public eBook(Context context) {super(context);Init(context);}public eBook(Context context, AttributeSet attrs) {super(context, attrs);Init(context);}}来源:清源教育。
Android用viewPager2实现UI界面翻页滚动的效果

Android⽤viewPager2实现UI界⾯翻页滚动的效果⽬录1.先在build.gradle(Module)下添加引⽤viewPager2的库2.在MainActivity下新建⼀个viewPager23.创建个ViewPagerAdapter1.先在build.gradle(Module)下添加引⽤viewPager2的库implementation 'androidx.viewpager2:viewpager2:1.0.0'2.在MainActivity下新建⼀个viewPager2ViewPager2 viewPager = findViewById(R.id.viewPager);ViewPagerAdapter viewPagerAdapter = new ViewPagerAdapter();viewPager.setAdapter(viewPagerAdapter);xml也要导⼊ViewPager2<androidx.viewpager2.widget.ViewPager2android:layout_width="match_parent"android:layout_height="match_parent"android:id="@+id/viewPager"android:background="@color/teal_200" ></androidx.viewpager2.widget.ViewPager2>3.创建个ViewPagerAdapterpackage com.zhizhu.test03;import youtInflater;import android.view.View;import android.view.ViewGroup;import android.widget.RelativeLayout;import android.widget.TextView;import androidx.annotation.NonNull;import androidx.recyclerview.widget.RecyclerView;import java.util.ArrayList;import java.util.List;public class ViewPagerAdapter extends RecyclerView.Adapter<ViewPagerAdapter.ViewPagerViewHolder> {private List<String> titles = new ArrayList<>();public ViewPagerAdapter(){titles.add("111");titles.add("222");titles.add("99999");titles.add("333");titles.add("99888");}@NonNull@Overridepublic ViewPagerViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {return new ViewPagerViewHolder(LayoutInflater.from(parent.getContext()).inflate(yout.item_pager, parent, false));}@Overridepublic void onBindViewHolder(@NonNull ViewPagerViewHolder holder, int position) {holder.mTV.setText(titles.get(position));}@Overridepublic int getItemCount() {return 5;}class ViewPagerViewHolder extends RecyclerView.ViewHolder{TextView mTV;RelativeLayout mContainer;public ViewPagerViewHolder(@NonNull View itemView) {super(itemView);mContainer = itemView.findViewById(R.id.container);mTV = itemView.findViewById(Title);}}}还要创建个item_pager.xml⽂件<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:id="@+id/container"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:id="@+id/tvTitle"android:layout_centerInParent="true"android:textColor="@color/black"android:textSize="30dp"android:text="88888"></TextView></RelativeLayout>最终效果图:以上就是Android⽤viewPager2实现UI界⾯翻页滚动的效果的详细内容,更多关于Android UI界⾯翻页滚动的资料请关注其它相关⽂章!。
Android翻页特效源码

package com.newBook;import java.util.Date;import android.content.Context;import android.graphics.Bitmap;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.LinearGradient;import android.graphics.Matrix;import android.graphics.Paint;import android.graphics.Path;import android.graphics.Point;import android.graphics.PorterDuffXfermode;import android.graphics.Shader;import android.graphics.PorterDuff.Mode;import android.os.Handler;import android.util.AttributeSet;import android.util.Log;import android.view.GestureDetector;import android.view.MotionEvent;import android.view.SurfaceHolder;import android.view.SurfaceV iew;import android.view.View;import android.view.GestureDetector.OnGestureListener; import android.widget.FrameLayout;import android.widget.LinearLayout;public class eBook extends FrameLayout{public static final String LOG_TAG = "eBook";int totalPageNum;Context mContext;boolean hasInit = false;final int defaultWidth = 600 , defaultHeight = 400;int contentWidth = 0;int contentHeight = 0;V iew leftPage,rightPage,llPage,lrPage,rrPage,rlPage; LinearLayout invisibleView;LinearLayout mView;MidView mMidView;bookV iew mBookView;Handler AniEndHandle;static boolean closeBook = false;private enum Corner {LeftTop,RightTop,LeftBottom,RightBottom,None};private Corner mSelectCorner;final int clickCornerLen = 250*250; //50dipfloat scrollX = 0,scrollY = 0;int indexPage = 0;public enum BookState {ABOUT_TO_ANIMA TE,ANIMA TING,ANIMA TE_END,READY,TRACKING};private BookState mState;private Point aniStartPos;private Point aniStopPos;private Date aniStartTime;private long aniTime = 800;private long timeOffset = 10;Listener mListener;PageAdapter mPageAdapter;private GestureDetector mGestureDetector;private BookOnGestureListener mGestureListener;public eBook(Context context) {super(context);Init(context);}public eBook(Context context, AttributeSet attrs) {super(context, attrs);Init(context);}public eBook(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle);Init(context);}public static interface Listener {public void onPrevPage();public void onNextPage();public void onInit();}public static interface PageAdapter{public V iew getView(int position);public int getCount();}public void setListener(Listener listener) {Log.d(LOG_TAG,"setListener");mListener = listener;}public void setPageAdapter(PageAdapter pa){Log.d(LOG_TAG,"setPageAdapter");mPageAdapter = pa;}public void Init(Context context){totalPageNum = 0;mContext = context;mSelectCorner = Corner.None;mGestureListener = new BookOnGestureListener(); mGestureDetector = new GestureDetector(mGestureListener); mGestureDetector.setIsLongpressEnabled(false); AniEndHandle = new Handler();this.setOnTouchListener(touchListener);this.setLongClickable(true);}public V iew getLeftPage(){return leftPage;}public V iew getRightPage(){return rightPage;}public V iew getPrevPage1(){return lrPage;}public V iew getPrevPage2(){return llPage;}public V iew getNextPage1(){return rlPage;}public V iew getNextPage2(){return rrPage;}public int getIndexForLeftPage(){return indexPage;}public int getIndexForRightPage(){return indexPage + 1;}public void setIndexForLeftPage(int index){if(index >= 0 && index < totalPageNum){if((index % 2) == 0)indexPage = index;elseindexPage = index - 1;}}public void updatePageView(){Log.d(LOG_TAG,"updatePageView");if(indexPage < 0 || indexPage > totalPageNum - 1){Log.d(LOG_TAG,"indexPage exceed bound");return;}invisibleView.removeAllViews();mV iew.removeAllViews();leftPage = mPageAdapter.getView(indexPage);if(leftPage == null)leftPage = new WhiteView(mContext);leftPage.setLayoutParams(new LayoutParams(contentWidth/2,contentHeight));mV iew.addView(leftPage);lrPage = mPageAdapter.getView(indexPage-1);if(lrPage == null)lrPage = new WhiteView(mContext);lrPage.setLayoutParams(new LayoutParams(contentWidth/2,contentHeight)); invisibleView.addView(lrPage);llPage = mPageAdapter.getView(indexPage-2);if(llPage == null)llPage = new WhiteView(mContext);llPage.setLayoutParams(new LayoutParams(contentWidth/2,contentHeight)); invisibleView.addView(llPage);rightPage = mPageAdapter.getView(indexPage+1);if(rightPage == null)rightPage = new WhiteView(mContext);rightPage.setLayoutParams(new LayoutParams(contentWidth/2,contentHeight)); mV iew.addView(rightPage);rlPage = mPageAdapter.getView(indexPage+2);if(rlPage == null)rlPage = new WhiteView(mContext);rlPage.setLayoutParams(new LayoutParams(contentWidth/2,contentHeight)); invisibleView.addView(rlPage);rrPage = mPageAdapter.getView(indexPage+3);if(rrPage == null)rrPage = new WhiteV iew(mContext);rrPage.setLayoutParams(new LayoutParams(contentWidth/2,contentHeight)); invisibleView.addView(rrPage);Log.d(LOG_TAG,"updatePageView finish");}OnTouchListener touchListener = new OnTouchListener() {public boolean onTouch(V iew v, MotionEvent event) {Log.d(LOG_TAG, "onTouch " + " x: " + event.getX() + " y: " + event.getY() + " mState:" + mState);mGestureDetector.onTouchEvent(event);int action = event.getAction();if (action == MotionEvent.ACTION_UP && mSelectCorner != Corner.None && mState == BookState.TRACKING) {if(mState == BookState.ANIMA TING)return false;if(mSelectCorner == Corner.LeftTop || mSelectCorner == Corner.RightTop){ if(scrollX < contentWidth/2){aniStopPos = new Point(0,0);}else{aniStopPos = new Point(contentWidth,0);}else if(mSelectCorner == Corner.LeftBottom || mSelectCorner == Corner.RightBottom){ if(scrollX < contentWidth/2){aniStopPos = new Point(0,contentHeight);}else{aniStopPos = new Point(contentWidth,contentHeight);}}aniStartPos = new Point((int)scrollX,(int)scrollY);aniTime = 800;mState = BookState.ABOUT_TO_ANIMA TE;closeBook = true;aniStartTime = new Date();mBookV iew.startAnimation();}return false;}};class BookOnGestureListener implements OnGestureListener {public boolean onDown(MotionEvent event) {Log.d(LOG_TAG, "onDown");if(mState == BookState.ANIMA TING)return false;float x = event.getX(), y = event.getY();int w = contentWidth, h = contentHeight;if(x*x + y*y < clickCornerLen){if(indexPage > 1){mSelectCorner = Corner.LeftTop;aniStartPos = new Point(0,0);}}else if((x-w)*(x-w)+ y*y < clickCornerLen){if(indexPage < totalPageNum-2){mSelectCorner = Corner.RightTop;aniStartPos = new Point(contentWidth,0);}}else if(x*x + (y-h)*(y-h) < clickCornerLen){if(indexPage > 1){mSelectCorner = Corner.LeftBottom;aniStartPos = new Point(0,contentHeight);}}else if((x-w)*(x-w) + (y-h)*(y-h) < clickCornerLen){if(indexPage < totalPageNum-2){mSelectCorner = Corner.RightBottom;aniStartPos = new Point(contentWidth,contentHeight);}if(mSelectCorner != Corner.None){aniStopPos = new Point((int)x,(int)y);aniTime = 800;mState = BookState.ABOUT_TO_ANIMA TE;closeBook = false;aniStartTime = new Date();mBookV iew.startAnimation();}return false;}public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { Log.d(LOG_TAG, "onFling veloc ityX:"+velocityX+" velocityY:"+velocityY);if(mSelectCorner != Corner.None){if(mSelectCorner == Corner.LeftTop || mSelectCorner == Corner.RightTop){if(velocityX < 0){aniStopPos = new Point(0,0);}else{aniStopPos = new Point(contentWidth,0);}}else if(mSelectCorner == Corner.LeftBottom || mSelectCorner == Corner.RightBottom){ if(velocityX < 0){aniStopPos = new Point(0,contentHeight);}else{aniStopPos = new Point(contentWidth,contentHeight);}}Log.d(LOG_TAG, "onFling animate");aniStartPos = new Point((int)scrollX,(int)scrollY);aniTime = 1000;mState = BookState.ABOUT_TO_ANIMA TE;closeBook = true;aniStartTime = new Date();mBookV iew.startAnimation();}return false;}public void onLongPress(MotionEvent e) {Log.d(LOG_TAG, "onLongPress");}public boolean onScroll(MotionEvent e1, MotionEvent e2,float distanceX, float distanceY) {// if(mState == BookState.ANIMA TING)return false;mState = BookState.TRACKING;if(mSelectCorner != Corner.None){scrollX = e2.getX();scrollY = e2.getY();mBookV iew.startAnimation();}return false;}public void onShowPress(MotionEvent e) {Log.d(LOG_TAG, "onShowPress");}public boolean onSingleTapUp(MotionEvent e) {Log.d(LOG_TAG, "onSingleTapUp");if(mSelectCorner != Corner.None){if(mSelectCorner == Corner.LeftTop || mSelectCorner == Corner.RightTop){if(scrollX < contentWidth/2){aniStopPos = new Point(0,0);}else{aniStopPos = new Point(contentWidth,0);}}else if(mSelectCorner == Corner.LeftBottom || mSelectCorner == Corner.RightBottom){ if(scrollX < contentWidth/2){aniStopPos = new Point(0,contentHeight);}else{aniStopPos = new Point(contentWidth,contentHeight);}}aniStartPos = new Point((int)scrollX,(int)scrollY);aniTime = 800;mState = BookState.ABOUT_TO_ANIMA TE;closeBook = true;aniStartTime = new Date();mBookV iew.startAnimation();}return false;}}protected void onFinishInflate() {Log.d(LOG_TAG, "onFinishInflate");super.onFinishInflate();}protected void onLayout(boolean changed, int l, int t, int r, int b) {super.onLayout(changed, l, t, r, b);contentWidth = this.getWidth();contentHeight = this.getHeight();if(contentWidth == 0) contentWidth = defaultWidth;if(contentHeight == 0) contentHeight = defaultHeight;Log.d(LOG_TAG, "onLayout, width:" + contentWidth + " height:" + contentHeight); }protected void dispatchDraw(Canvas canvas) {Log.d(LOG_TAG, "dispatchDraw");super.dispatchDraw(canvas);if(!hasInit){hasInit = true;indexPage = 0;if(mPageAdapter == null){throw new RuntimeException("please set the PageAdapter on init");}totalPageNum = mPageAdapter.getCount();mV iew = new LinearLayout(mContext);mV iew.setLayoutParams(new LayoutParams(contentWidth,contentHeight));mV iew.setBackgroundColor(0xffffffff);mState = BookState.READY;invisibleView = new LinearLayout(mContext);invisibleView.setLayoutParams(new LayoutParams(contentWidth,contentHeight));mMidV iew = new MidView(mContext);mMidV iew.setLayoutParams(new LayoutParams(contentWidth,contentHeight));this.addV iew(invisibleView);this.addV iew(mView);this.addV iew(mMidView);mBookV iew = new bookView(mContext);mBookV iew.setLayoutParams(new LayoutParams(contentWidth,contentHeight));this.addV iew(mBookView);// invisibleView.setVisibility(INVISIBLE);// mBookView.setVisibility(GONE);updatePageView();invalidate();if(mListener != null)mListener.onInit();}else if (mState == BookState.READY){mBookV iew.update();}}class bookV iew extends SurfaceV iew implements SurfaceHolder.Callback{DrawThread dt; //后台屏幕绘制线程SurfaceHolder surfaceHolder;Paint mDarkPaint = new Paint();Paint mPaint = new Paint();Bitmap tmpBmp = Bitmap.createBitmap(contentWidth/2,contentHeight,Bitmap.Config.ARGB_8888);Canvas mCanvas = new Canvas(tmpBmp);Paint bmpPaint = new Paint();Paint ivisiblePaint = new Paint();public bookView(Context context) {super(context);surfaceHolder = getHolder();surfaceHolder.addCallback(this);mDarkPaint.setColor(0x88000000);Shader mLinearGradient = new LinearGradient(0, 0, contentWidth, 0,new int[] { 0x00000000, 0x33000000,0x00000000 },new float[] { 0.35f, 0.5f, 0.65f },Shader.TileMode.MIRROR);mPaint.setAntiAlias(true);mPaint.setShader(mLinearGradient);bmpPaint.setFilterBitmap(true);bmpPaint.setAntiAlias(true);ivisiblePaint.setAlpha(0);ivisiblePaint.setFilterBitmap(true);ivisiblePaint.setAntiAlias(true);ivisiblePaint.setXfermode(new PorterDuffXfermode(Mode.DST_IN));}public void startAnimation(){// update();if(dt == null){Log.d(LOG_TAG,"startAnimation");// mBookView.setVisibility(VISIBLE);dt = new DrawThread(this,getHolder());dt.start();}}public void stopAnimation(){Log.d(LOG_TAG,"stopAnimation");// AniEndHandle.post(new Runnable(){// public void run() {// mBookView.setVisibility(GONE);// }// });if(dt != null){dt.flag = false;Thread t = dt;dt = null;t.interrupt();}}public void drawLT(Canvas canvas){double dx = scrollX - contentWidth/2, dy = scrollY;double len = Math.sqrt(dx*dx + dy*dy);if(len > contentWidth/2){scrollX = (float) (contentWidth*dx/(2.0*len)) + contentWidth/2;scrollY = (float) (contentWidth*dy/(2.0*len));}dy = contentHeight - scrollY;len = Math.sqrt(dx*dx + dy*dy);double d = Math.sqrt(contentWidth*contentWidth/4 + contentHeight*contentHeight);if(len > d){scrollX = (float) (d*dx/len) + contentWidth/2;scrollY = contentHeight - (float) (d*dy/len);}double px = scrollX;double py = scrollY;double arc = 2 * Math.atan(py/px)*180/Math.PI;Matrix m = new Matrix();m.postTranslate(scrollX - contentWidth/2, scrollY);m.postRotate((float)(arc),scrollX,scrollY);lrPage.draw(mCanvas);Paint ps = new Paint();Shader lg1 = new LinearGradient(contentWidth/2, 0, contentWidth/2 - (float)px,(float)py,new int[] { 0x00000000, 0x33000000,0x00000000 },new float[] { 0.35f, 0.5f, 0.65f },Shader.TileMode.CLAMP);ps.setShader(lg1);mCanvas.drawRect(0, 0, contentWidth/2, contentHeight, ps); canvas.drawBitmap(tmpBmp, m, bmpPaint);llPage.draw(mCanvas);Shader lg2 = new LinearGradient(scrollX,scrollY,0, 0,new int[] { 0x00000000, 0x33000000,0x00000000 }, new float[] { 0.35f, 0.5f, 0.65f },Shader.TileMode.CLAMP);ps.setShader(lg2);mCanvas.drawRect(0, 0, contentWidth/2, contentHeight, ps);arc = arc*Math.PI/360;Path path = new Path();double r = Math.sqrt(px * px + py * py);double p1 = r/(2*Math.cos(arc));double p2 = r/(2*Math.sin(arc));Log.d(LOG_TAG,"p1: " + p1 + " p2:" + p2);if(arc == 0){path.moveTo((float)p1, 0);path.lineTo(contentWidth/2, 0);path.lineTo(contentWidth/2, contentHeight);path.lineTo((float)p1, contentHeight);path.close();}else if(p2 > contentHeight || p2 < 0){double p3 = (p2 - contentHeight)*Math.tan(arc);path.moveTo((float)p1, 0);path.lineTo(contentWidth/2, 0);path.lineTo(contentWidth/2, contentHeight);path.lineTo((float)p3, contentHeight);path.close();}else{path.moveTo((float)p1, 0);path.lineTo(contentWidth/2, 0);path.lineTo(contentWidth/2, contentHeight);path.lineTo(0, contentHeight);path.lineTo(0, (float)p2);path.close();}mCanvas.drawPath(path, ivisiblePaint);canvas.drawBitmap(tmpBmp, 0, 0, null);}public void drawLB(Canvas canvas){double dx = scrollX - contentWidth/2, dy = scrollY - contentHeight;double len = Math.sqrt(dx*dx + dy*dy);if(len > contentWidth/2){scrollX = (float) (contentWidth*dx/(2.0*len)) + contentWidth/2;scrollY = (float) (contentWidth*dy/(2.0*len)) + contentHeight;}dy = scrollY;len = Math.sqrt(dx*dx + dy*dy);double d = Math.sqrt(contentWidth*contentWidth/4 + contentHeight*contentHeight); if(len > d){scrollX = (float) (d*dx/len) + contentWidth/2;scrollY = (float) (d*dy/len);}double px = scrollX;double py = contentHeight - scrollY;double arc = 2 * Math.atan(py/px)*180/Math.PI;Matrix m = new Matrix();m.postTranslate(scrollX - contentWidth/2, scrollY - contentHeight);m.postRotate((float)(-arc),scrollX,scrollY);lrPage.draw(mCanvas);Paint ps = new Paint();Shader lg1 = new LinearGradient(contentWidth/2, contentHeight,contentWidth/2 - (float)px, contentHeight - (float)py,new int[] { 0x00000000, 0x33000000,0x00000000 },new float[] { 0.35f, 0.5f, 0.65f },Shader.TileMode.CLAMP);ps.setShader(lg1);mCanvas.drawRect(0, 0, contentWidth/2, contentHeight, ps);canvas.drawBitmap(tmpBmp, m, bmpPaint);llPage.draw(mCanvas);Shader lg2 = new LinearGradient(scrollX,scrollY,0, contentHeight,new int[] { 0x00000000, 0x33000000,0x00000000 },new float[] { 0.35f, 0.5f, 0.65f },Shader.TileMode.CLAMP);ps.setShader(lg2);mCanvas.drawRect(0, 0, contentWidth/2, contentHeight, ps);arc = arc*Math.PI/360;Path path = new Path();double r = Math.sqrt(px * px + py * py);double p1 = r/(2*Math.cos(arc));double p2 = r/(2*Math.sin(arc));Log.d(LOG_TAG,"p1: " + p1 + " p2:" + p2);if(arc == 0){path.moveTo((float)p1, 0);path.lineTo(contentWidth/2, 0);path.lineTo(contentWidth/2, contentHeight);path.lineTo((float)p1, contentHeight);path.close();}else if(p2 > contentHeight || p2 < 0){double p3 = (p2 - contentHeight)*Math.tan(arc);path.moveTo((float)p3, 0);path.lineTo(contentWidth/2, 0);path.lineTo(contentWidth/2, contentHeight);path.lineTo((float)p1, contentHeight);path.close();}else{path.moveTo(0, 0);path.lineTo(contentWidth/2, 0);path.lineTo(contentWidth/2, contentHeight);path.lineTo((float)p1, contentHeight);path.lineTo(0, contentHeight - (float)p2);path.close();}mCanvas.drawPath(path, ivisiblePaint);canvas.drawBitmap(tmpBmp, 0, 0, null);}public void drawRT(Canvas canvas){double dx = scrollX - contentWidth/2, dy = scrollY;double len = Math.sqrt(dx*dx + dy*dy);if(len > contentWidth/2){scrollX = (float) (contentWidth*dx/(2.0*len)) + contentWidth/2; scrollY = (float) (contentWidth*dy/(2.0*len));}dy = contentHeight - scrollY;len = Math.sqrt(dx*dx + dy*dy);double d = Math.sqrt(contentWidth*contentWidth/4 + contentHeight*contentHeight); if(len > d){scrollX = (float) (d*dx/len) + contentWidth/2;scrollY = contentHeight - (float) (d*dy/len);}double px = contentWidth - scrollX;double py = scrollY;double arc = 2 * Math.atan(py/px)*180/Math.PI;Matrix m = new Matrix();m.postTranslate(scrollX, scrollY);m.postRotate((float)(-arc),scrollX,scrollY);rlPage.draw(mCanvas);Paint ps = new Paint();Shader lg1 = new LinearGradient(0, 0, (float)px, (float)py,new int[] { 0x00000000, 0x33000000,0x00000000 },new float[] { 0.35f, 0.5f, 0.65f },Shader.TileMode.CLAMP);ps.setShader(lg1);mCanvas.drawRect(0, 0, contentWidth/2, contentHeight, ps);canvas.drawBitmap(tmpBmp, m, bmpPaint);rrPage.draw(mCanvas);Shader lg2 = new LinearGradient(scrollX-contentWidth/2, scrollY,contentWidth/2, 0,new int[] { 0x00000000, 0x33000000,0x00000000 },new float[] { 0.35f, 0.5f, 0.65f },Shader.TileMode.CLAMP);ps.setShader(lg2);mCanvas.drawRect(0, 0, contentWidth/2, contentHeight, ps);arc = arc*Math.PI/360;Path path = new Path();double r = Math.sqrt(px * px + py * py);double p1 = contentWidth/2 - r/(2*Math.cos(arc));double p2 = r/(2*Math.sin(arc));Log.d(LOG_TAG,"p1: " + p1 + " p2:" + p2);if(arc == 0){path.moveTo(0, 0);path.lineTo((float)p1, 0);path.lineTo((float)p1, contentHeight);path.lineTo(0, contentHeight);path.close();}else if(p2 > contentHeight || p2 < 0){double p3 = contentWidth/2 - (p2 - contentHeight)*Math.tan(arc);path.moveTo(0, 0);path.lineTo((float)p1, 0);path.lineTo((float)p3, contentHeight);path.lineTo(0, contentHeight);path.close();}else{path.moveTo(0, 0);path.lineTo((float)p1, 0);path.lineTo(contentWidth/2, (float)p2);path.lineTo(contentWidth/2, contentHeight);path.lineTo(0, contentHeight);path.close();}mCanvas.drawPath(path, ivisiblePaint);canvas.drawBitmap(tmpBmp, contentWidth/2, 0, null);}public void drawRB(Canvas canvas){double dx = scrollX - contentWidth/2, dy = contentHeight - scrollY;double len = Math.sqrt(dx*dx + dy*dy);if(len > contentWidth/2){scrollX = (float) (contentWidth*dx/(2.0*len)) + contentWidth/2;scrollY = contentHeight - (float) (contentWidth*dy/(2.0*len));}dy = scrollY;len = Math.sqrt(dx*dx + dy*dy);double d = Math.sqrt(contentWidth*contentWidth/4 + contentHeight*contentHeight); if(len > d){scrollX = (float) (d*dx/len) + contentWidth/2;scrollY = (float) (d*dy/len);}double px = contentWidth - scrollX;double py = contentHeight - scrollY;double arc = 2 * Math.atan(py/px)*180/Math.PI;Matrix m = new Matrix();m.postTranslate(scrollX, scrollY - contentHeight);m.postRotate((float)(arc),scrollX,scrollY);rlPage.draw(mCanvas);Paint ps = new Paint();Shader lg1 = new LinearGradient(0, contentHeight,(float)px,contentHeight-(float)py,new int[] { 0x00000000, 0x33000000,0x00000000 },new float[] { 0.35f, 0.5f, 0.65f },Shader.TileMode.CLAMP);ps.setShader(lg1);mCanvas.drawRect(0, 0, contentWidth/2, contentHeight, ps); canvas.drawBitmap(tmpBmp, m, bmpPaint);rrPage.draw(mCanvas);Shader lg2 = new LinearGradient(scrollX-contentWidth/2, scrollY, contentWidth/2, contentHeight,new int[] { 0x00000000, 0x33000000,0x00000000 },new float[] { 0.35f, 0.5f, 0.65f },Shader.TileMode.CLAMP);ps.setShader(lg2);mCanvas.drawRect(0, 0, contentWidth/2, contentHeight, ps);arc = arc*Math.PI/360;Path path = new Path();double r = Math.sqrt(px * px + py * py);double p1 = contentWidth/2 - r/(2*Math.cos(arc));double p2 = r/(2*Math.sin(arc));Log.d(LOG_TAG,"p1: " + p1 + " p2:" + p2);if(arc == 0){path.moveTo(0, 0);path.lineTo((float)p1, 0);path.lineTo((float)p1, contentHeight);path.lineTo(0, contentHeight);path.close();}else if(p2 > contentHeight || p2 < 0){double p3 = contentWidth/2 - (p2 - contentHeight)*Math.tan(arc); path.moveTo(0, 0);path.lineTo((float)p3, 0);path.lineTo((float)p1, contentHeight);path.lineTo(0, contentHeight);path.close();}else{path.moveTo(0, 0);path.lineTo(contentWidth/2, 0);path.lineTo(contentWidth/2, contentHeight - (float)p2);path.lineTo((float)p1, contentHeight);path.lineTo(0, contentHeight);path.close();}mCanvas.drawPath(path, ivisiblePaint);canvas.drawBitmap(tmpBmp, contentWidth/2, 0, null);}public void drawPrevPageEnd(Canvas canvas){llPage.draw(mCanvas);canvas.drawBitmap(tmpBmp, 0, 0, null);lrPage.draw(mCanvas);canvas.drawBitmap(tmpBmp, contentWidth/2, 0, null);canvas.drawRect(0, 0, contentWidth, contentHeight, mPaint);canvas.drawLine(contentWidth/2, 0, contentWidth/2, contentHeight, mDarkPaint); }public void drawNextPageEnd(Canvas canvas) {rlPage.draw(mCanvas);canvas.drawBitmap(tmpBmp, 0, 0, null);rrPage.draw(mCanvas);canvas.drawBitmap(tmpBmp, contentWidth / 2, 0, null);canvas.drawRect(0, 0, contentWidth, contentHeight, mPaint);canvas.drawLine(contentWidth/2, 0, contentWidth/2, contentHeight, mDarkPaint); }public void drawPage(Canvas canvas){if(mSelectCorner == Corner.LeftTop){Log.d(LOG_TAG,"click left top");drawLT(canvas);}else if(mSelectCorner == Corner.LeftBottom){Log.d(LOG_TAG,"click left bottom");drawLB(canvas);}else if(mSelectCorner == Corner.RightTop){Log.d(LOG_TAG,"click right top");drawRT(canvas);}else if(mSelectCorner == Corner.RightBottom){Log.d(LOG_TAG,"click right bottom");drawRB(canvas);}}public void update(){Canvas canvas = surfaceHolder.lockCanvas(null);//获取BallView的画布try{synchronized(surfaceHolder){doDraw(canvas); //调用BallView的doDraw方法进行绘制}}catch(Exception e){e.printStackTrace(); //捕获并打印异常}finally{if(canvas != null){ //如果canvas不为空surfaceHolder.unlockCanvasAndPost(canvas);//surfaceHolder解锁并将画布对象传回}}}protected void doDraw(Canvas canvas) {Log.d(LOG_TAG, "bookV iew doDraw");// super.onDraw(canvas);mV iew.draw(canvas);canvas.drawRect(0, 0, contentWidth, contentHeight, mPaint);canvas.drawLine(contentWidth/2, 0, contentWidth/2, contentHeight, mDarkPaint);}public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}public void surfaceCreated(SurfaceHolder holder) {update();}public void surfaceDestroyed(SurfaceHolder holder) {if(dt != null){dt.flag = false; //停止线程的执行dt = null; //将dt指向的对象声明为垃圾}}}public boolean getAnimateData(){long time = aniTime;Date date = new Date();long t = date.getTime()-aniStartTime.getTime();t += timeOffset;if(t < 0 || t > time){mState = BookState.ANIMA TE_END;return false;}else{mState = BookState.ANIMA TING;double sx = aniStopPos.x - aniStartPos.x;double gx = 2*sx/(time*time);scrollX = (float) (gx*time*t - gx*t*t/2 + aniStartPos.x);double sy = aniStopPos.y - aniStartPos.y;double gy = 2*sy/(time*time);scrollY = (float) (gy*time*t - gy*t*t/2 + aniStartPos.y);return true;}}public void handleAniEnd(Canvas canvas){if(closeBook){closeBook = false;if(mSelectCorner == Corner.LeftTop || mSelectCorner == Corner.LeftBottom){if(scrollX > contentWidth/2){Log.d(LOG_TAG,"handleAniEnd -2,indexPage:"+ (indexPage-2));indexPage -= 2;mBookV iew.drawPrevPageEnd(canvas);AniEndHandle.post(new Runnable(){public void run() {updatePageView();if(mListener != null){Log.d(LOG_TAG,"onPrevPage");mListener.onPrevPage();}}});}else{mBookV iew.doDraw(canvas);}}else if(mSelectCorner == Corner.RightTop || mSelectCorner == Corner.RightBottom){ if(scrollX < contentWidth/2){Log.d(LOG_TAG,"handleAniEnd +2,indexPage:"+ (indexPage+2));indexPage += 2;mBookV iew.drawNextPageEnd(canvas);AniEndHandle.post(new Runnable(){public void run() {updatePageView();if(mListener != null){Log.d(LOG_TAG,"onNextPage");mListener.onNextPage();}}});}else{mBookV iew.doDraw(canvas);}}mSelectCorner = Corner.None; mState = BookState.READY;}else{mState = BookState.TRACKING;}mBookV iew.stopAnimation(); AniEndHandle.post(new Runnable(){ public void run() {eBook.this.invalidate();}});}class WhiteView extends V iew{public WhiteView(Context context) { super(context);}protected void onDraw(Canvas canvas) { super.onDraw(canvas);canvas.drawColor(Color.WHITE);}}class MidView extends V iew{public MidView(Context context) { super(context);}protected void onDraw(Canvas canvas) { super.onDraw(canvas);Paint mPaint = new Paint();Shader mLinearGradient = new LinearGradient(0, 0, contentWidth, 0,new int[] { 0x00000000, 0x33000000,0x00000000 },new float[] { 0.35f, 0.5f, 0.65f },Shader.TileMode.MIRROR);mPaint.setAntiAlias(true);mPaint.setShader(mLinearGradient);canvas.drawRect(0, 0, contentWidth, contentHeight, mPaint);Paint mDarkPaint = new Paint();mDarkPaint.setColor(0x88000000);canvas.drawLine(contentWidth/2, 0, contentWidth/2, contentHeight, mDarkPaint);}}public class DrawThread extends Thread{bookV iew bv; //bookV iew对象引用SurfaceHolder surfaceHolder;//SurfaceHolder对象引用boolean flag=false; //线程执行标志位int sleepSpan = 30; //休眠时间public DrawThread(bookV iew bv,SurfaceHolder surfaceHolder){this.bv = bv; //为BallView对象应用赋值this.surfaceHolder = surfaceHolder; //为SurfaceHolder对象应用赋值this.flag = true; //设置标志位}public void run(){Canvas canvas = null;//声明一个Canvas对象while(flag){try{canvas = surfaceHolder.lockCanvas(null);//获取BallView的画布if(canvas == null)continue;synchronized(surfaceHolder){if(mState == BookState.ABOUT_TO_ANIMA TE || mState == BookState.ANIMA TING){ bv.doDraw(canvas); //调用BallView的doDraw方法进行绘制getAnimateData();bv.drawPage(canvas);}else if(mState == BookState.TRACKING){bv.doDraw(canvas);bv.drawPage(canvas);}else if(mState == BookState.ANIMA TE_END){handleAniEnd(canvas);}}}。
在android中实现手势翻页效果

在android中实现手势翻页效果,主要用到ViewFlipper和Gesture Detector.ViewFlipper变化当前显示内容,Gestu reDetector监听手势.用于多页的展示非常酷.以下是简略说明:首先创建工程:TestFlip,创建主Activity:TestFlip.在res/layout/main.xml中添加flipper信息,如下:Java代码1. < ?xml ve rsion="1.0" encoding="utf-8"?>2. < Linea rLayo ut xmlns:android="/apk/res/android"3. and roid:orientation="ve rtical"4. and roid:layout_width="fill_parent"5. and roid:layout_height="fill_parent"6. >7. < View Flipper android:id="@+id/View Flipper01"8. and roid:layout_width="fill_parent" android:layo ut_height="fill_parent">9. < /View Flipper>10. < /Linea rLayout>< ?xml ve rsion="1.0" encoding="utf-8"?>< LinearL ayout xmlns:android="/apk/res/android"android:o rientation="ve rtical"android:la yout_width="fill_parent"android:la yout_height="fill_parent">< ViewFlipper a ndroid:id="@+id/ViewFlipper01"android:la yout_width="fill_parent" an droid:layout_height="fill_parent">< /ViewFlipper>< /LinearLayout>然后将TestFlip实现OnGestureListener接口,并实现所有抽象方法,然后开始改造这个类.首先,声明两个私有成员.Java代码1. pri vate ViewFlipper flipper;//View Flipper实例2. pri vate GestureDetecto r detector;//触摸监听实例private ViewFlipper flipper;//View Flipper实例private GestureDetecto r detector;//触摸监听实例然后在onCreate方法中添加成员初始化.Java代码1. detector = new Gesture Detector(this);//初始化触摸探测2. flipper = (ViewFlipper) t his.findViewById(R.i d.ViewFlipper01);//获得View Flipper实例3.4. flipper.addView(addTextView("step 1"));//将View添加到flipper队列中5. flipper.addView(addTextView("step 2"));6. flipper.addView(addTextView("step 3"));7. flipper.addView(addTextView("step 4"));8. flipper.addView(addTextView("step 5"));detector = new GestureD etector(this);//初始化触摸探测flipper = (View Flipper) this.findViewById(R.id.ViewFlipper01);//获得View Flipper实例flipper.addView(ad dTextView("step 1"));//将View添加到flipper队列中flipper.addView(ad dTextView("step 2"));flipper.addView(ad dTextView("step 3"));flipper.addView(ad dTextView("step 4"));flipper.addView(ad dTextView("step 5"));addTextView方法如下:Java代码1. pri vate View addTextView(String te xt) {2. TextView t v = ne w TextView(this);3. t v.setText(te xt);4. t v.setGravit y(1);5. retu rn t v;6. }private View addTextView(String te xt) {TextView t v = new Te xtView(this);tv.setText(te xt);tv.set Gra vity(1);retu rn t v;}flipper将按照你的添加顺序排列这些View,并通过flipper.showNe xt()和flipper.showPre vious()显示.还需要多重写一个方法:o nTouchEvent(MotionE vent e vent),否则detector检测不到触摸,这个方法非常简单. Java代码1. @Override2. public boolean onTouchEvent(MotionE vent e vent) {3. retu rn this.detector.onTouchEvent(e vent);4. }@Overridepublic boolean onTouchEvent(MotionEve nt eve nt) {retu rn this.detector.onTouchEvent(eve nt);}现在开始做动作监听,在on Fling方法中加入以下内容:Java代码1. @Override2. public boolean onFling(MotionEvent e1, MotionEve nt e2, float velocityX,3. float velocityY) {4. this.flipper.showNext();//显示flipper中的下一个view5. retu rn t rue;6. }@Overridepublic boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,float velocityY) {this.flipper.showNext();//显示flipper中的下一个viewretu rn tru e;}现在可以运行一下看看效果了.你会发现当鼠标滑动时画面只是很简单的从Step 1变成Step 2,并没有那种画面滑动的效果,而且无论你从左向右滑动还是从右向左滑动都是按照同一个顺序,现在我们修改一些,让效果更炫一点.先在res目录下创建anim目录,并创建4个基于Animation的xml文件,分别命名为:left_in.xml,left_out.xml,right_in.xml,right_left.xml内容分别为:left_in.xml:Java代码1. < ?xml ve rsion="1.0" encoding="utf-8"?>2. < set xmlns:android="/ap k/res/android">3. < t ranslate android:fro mXDelta="100%p" and roid:toXDelta="0"4. and roid:duration="500" />5. < /set>< ?xml ve rsion="1.0" encoding="utf-8"?>< set xmlns:android="h ttp:///apk/res/android">< translate android:fromX Delta="100%p" android:toXDelta="0"android:du ration="500" />< /set>left_out.xml:Java代码1. < ?xml ve rsion="1.0" encoding="utf-8"?>2. < set xmlns:android="/ap k/res/android">3. < t ranslate android:fro mXDelta="0" and roid:toXDelta="-100%p"4. and roid:duration="500" />5. < /set>< ?xml ve rsion="1.0" encoding="utf-8"?>< set xmlns:android="h ttp:///apk/res/android">< translate android:fromX Delta="0" and roid:toXDelta="-100%p"android:du ration="500" />< /set>right_in.xml:Java代码1. < ?xml ve rsion="1.0" encoding="utf-8"?>2. < set xmlns:android="/ap k/res/android">3. < t ranslate android:fro mXDelta="-100%p" a ndroid:toXDelta="0"4. and roid:duration="500" />5. < /set>< ?xml ve rsion="1.0" encoding="utf-8"?>< set xmlns:android="h ttp:///apk/res/android">< translate android:fromX Delta="-100%p" and roid:toXDelta="0"android:du ration="500" />< /set>right_out.xml:Java代码1. < ?xml ve rsion="1.0" encoding="utf-8"?>2. < set xmlns:android="/ap k/res/android">3. < t ranslate android:fro mXDelta="0" and roid:toXDelta="100%p"4. and roid:duration="500" />5. < /set>< ?xml ve rsion="1.0" encoding="utf-8"?>< set xmlns:android="h ttp:///apk/res/android">< translate android:fromX Delta="0" and roid:toXDelta="100%p"android:du ration="500" />< /set>主要是做一个translation动画,fro mXDelta:动画的开始X位置,toX Delta:动画的结束X位置,duration:持续时间.然后将onFling方法修改为如下:Java代码1. @Override2. public boolean onFling(MotionEvent e1, MotionEve nt e2, float velocityX,3. float velocityY) {4. if (e1.getX() - e2.getX() > 120) {//如果是从右向左滑动5. //注册flipper的进出效果6. this.flipper.setInAnimation(AnimationUtils.loadAnimation(this, R.anim.left_in));7. this.flipper.setOutAnimation(AnimationUtils.loadAnimation(this, R.anim.left_out));8. this.flipper.showNext();9. retu rn t rue;10. } else if (e1.getX() - e2.getX() < -120) {//如果是从左向右滑动11. this.flipper.setInAnimation(AnimationUtils.loadAnimation(this, R.anim.right_in));12. this.flipper.setOutAnimation(AnimationUtils.loadAnimation(this, R.anim.right_out));13. this.flipper.showPre vious();14. ret urn t rue;15. }16. ret urn false;17. }@Overridepublic boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,float velocityY) {if (e1.getX() - e2.getX() > 120) {//如果是从右向左滑动//注册flipper的进出效果this.flipper.setInAnimation(AnimationUtils.loadAnimat ion(this, R.anim.left_in));this.flipper.setOutAnimation(AnimationUtils.loadAnimation(this, R.anim.left_out));this.flipper.showNext();retu rn tru e;} else if (e1.getX() - e2.getX() < -120) {//如果是从左向右滑动this.flipper.setInAnimation(AnimationUtils.loadAnimat ion(this, R.anim.right_in));this.flipper.setOutAnimation(AnimationUtils.loadAnimation(this, R.anim.right_out));this.flipper.showPrevious();retu rn tru e;}retu rn false;}然后重新运行看看效果,如果图片之类的多,还可以在animation里加入alpha的效果,如下Java代码1. < alpha and roid:fromAlpha="0.1" and roid:toAlpha="1.0"2. and roid:duration="500" />< alpha and roid:fromAlpha="0.1" android:toAlpha="1.0" android:du ration="500" />一个手势翻页效果就搞定了,用在多内容的展示效果上会非常棒.。
Android自定义ViewPager实现纵向滑动翻页效果

Android⾃定义ViewPager实现纵向滑动翻页效果抖⾳⼏乎已经成为了我们⽇常⽣活中使⽤⽐较频繁的App,⽆聊之时或⼯作之后可以刷⼀刷短视频来供我们娱乐与放松。
看到抖⾳的视屏切换效果,觉得⽤ViewPager可以做出⼀样的效果。
想⼀想之前⽤的ViewPager都是横向切换的,虽然很经常⽤,但是从来没实现过竖向的切换效果,说做就做吧。
我们先看⼀波效果图:那么,要想实现这样的效果,当然是⾃定义ViewPager啦。
问了⼀下度娘,看到有这样⼀种思路:⾸先,把Touch事件的x,y坐标做⼀下交换,从原先的x坐标差值转变成y坐标的差值,正符合了我们⼿指从横向滑动转成了纵向滑动。
其次,再通过实现PageTransformer接⼝,改变view的平移⽅向,便实现了如上效果。
下⾯我们贴出代码,并理解⼀下整个实现过程。
public class VerticalViewPager extends ViewPager {public VerticalViewPager(Context context) {super(context);}public VerticalViewPager(Context context, AttributeSet attrs) {super(context, attrs);}@Overridepublic boolean onTouchEvent(MotionEvent ev) {return super.onTouchEvent(swapTouchEvent(MotionEvent.obtain(ev)));}@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {return super.onInterceptTouchEvent(swapTouchEvent(MotionEvent.obtain(ev)));}private MotionEvent swapTouchEvent(MotionEvent event) {float width = getWidth();float height = getHeight();event.setLocation((event.getY() / height) * width, ((event.getX() / width) * height));return event;}}⾸先,我们⾃定义⼀个继承ViewPager的类,我们重点关注⼀下swapTouchEvent()⽅法。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
第二种翻页效果原理
根据第一种翻页效果原理可以确定a 、e、h、f、g 由eh平行于cj且af垂直于eh则 af垂直于cj则三角形egf相似于三角形 cnf 则有ef:cf = gf:nf 设n为ag中点 则有cf=(3/2)*ef ,则c点坐标可求
欢迎加入android开发讨论组 由c点、g点可确定过两点间的直线 【68114641】 当该直线中x=0时求出与y足交点
第二种翻页效果
欢迎加入android开发讨论组 【68114641】
第二种翻页与一之间区别
第一种翻页效果 第二种翻页效果
贝赛尔曲线
无
有
贝赛尔曲线 转到维基百科: 具个人能力有限不是很清楚怎么绘制但是我对怎么绘制的 理解 首先确定四个点:起始点、控制点、定点、结束点 android中提供两个点绘制贝塞尔曲线的方法
Android翻页效果实现技术分享
• • •
•
两种翻页效果 两种翻页效果实现原理 两种翻页效果之间的区别 Android平台中翻页效果实现
欢迎加入android开发讨论组 【68114641】
简介
n n
两种翻页效果实现原理 Android平台下翻页效果实现
欢迎加入android开发讨论组 【68114641】
由c点、k点坐标已知可知过两点间的 直线 由该直线可计算与y轴相交点j
由a、e、c、Βιβλιοθήκη 可计算两条直线的相交 点b 同理可求点k 欢迎加入android开发讨论组
【68114641】
Android中具体实现
起始页展示
1.创建屏幕尺寸的bmp 2.将图片转化为canvas 3.获取起始页面数据 3.在canvas中绘制起始页数据 4.在当前视图中复写onDraw进行重绘出 bmp对象
第一种翻页效果
欢迎加入android开发讨论组 【68114641】
翻页原理
当前手指触摸点为a则 a点坐标为(ax,ay)
由三角形acb与三角形cmb为对称三角 形并且直线cp为am垂直平分线则 B点坐标为 (ax/2,ay/2)
作gf垂直于om且cb垂直于am 三角形cfg与gfm相似 则 cf:gf = gf:mf cf=(gf * gf) / mf gf长度为g点纵坐标 mf长度为g点横坐标 cf长度可求 c点坐标可求
欢迎加入android开发讨论组 【68114641】
欢迎加入android开发讨论组 【68114641】
Android中具体实现
翻页处理
1.初始化时创建两个bmp(bmp1、bmp2)并将其转换为 canvas(canvas1、canvas2) 2.获取手势首次触摸的区域 (例:当首次点击屏幕的位置x<50&&y<50则为左上角) 3.根据首次点击区域判断需要展示的数据(例:首次点击处于左侧 区域【左上、左下】的则判断操作为下一页操作) 4.获取下一页中数据并绘制出来在canvas2中 5.根据1中获取的区域位置调用起始动画使视图移动到手势首次点击 位置 6.获取手势每次移动的坐标并根据移动坐标计算绘制的各个点的坐 标 7.每次移动刷新视图