Android自定义View——从零开始实现书籍翻页效果(二)

版权声明:本文为博主原创文章,未经博主容许不得转载html

系列教程:Android开发之从零开始系列java

源码:AnliaLee/BookPage,欢迎starandroid

你们要是看到有错误的地方或者有啥好的建议,欢迎留言评论git

前言:在上篇Android自定义View——从零开始实现书籍翻页效果(一)博客中,咱们实现了 基本的上下翻页效果右侧最大翻页距离的限制,这期咱们要将这个view的翻页效果以及动画补齐github

本篇只着重于思路和实现步骤,里面用到的一些知识原理不会很是细地拿来说,若是有不清楚的api或方法能够在网上搜下相应的资料,确定有大神讲得很是清楚的,我这就不献丑了。本着认真负责的精神我会把相关知识的博文连接也贴出来(其实就是懒不想写那么多哈哈),你们能够自行传送。为了照顾第一次阅读系列博客的小伙伴,本篇会出现一些在以前系列博客就讲过的内容,看过的童鞋自行跳过该段便可api

国际惯例,先上效果图,本次主要补全了翻页效果以及增长取消翻页的动画ide


完善右侧最大翻页距离的限制

开讲以前,我先把标识点的位置图贴出来让你们回顾一下post

在上篇博客中咱们讲了如何限制翻页的最大距离,也就是c点的x坐标不能小于0,虽然目的达到了,可是用户体验并很差,能够很明显地观察到当c点x坐标处于临界值时,翻页会静止不动,若是此时触摸点大范围移动后,会出现翻页“瞬移”,形成一种卡顿的感受,以下图学习

要消除这种“瞬移”的现象,咱们要在c点x坐标小于0的状况下,让c点强制处于临界位置(左下角),而后翻页页角继续跟随触摸点移动的方向移动,要作到这一点得用到一些类似三角形的数学知识,从新计算出a点的位置绘制View,先来看下实现的原理(请无视个人渣画工╮(╯▽╰)╭ )动画

图中咱们将触摸点标为a1,与a1对应的c点标为c1,此时c1的x坐标是小于0。而a2是咱们从新计算获得的a点,也就是最后进行绘制的a点,其对应的c点标为c2c2位于View的左下角(x坐标为0),容易观察到直角三角形a1 c1 m1直角三角形a2 c2 m2类似,此时f点位于View的右下角(在右上角同理),所以咱们能够经过相应的公式计算获得a2的坐标为(f.x-w2,f.y-h2),获得计算a2的方法后,咱们修改原来的BookPageView

/** * 设置触摸点 * @param x * @param y * @param style */
public void setTouchPoint(float x, float y, String style){
	switch (style){
		case STYLE_TOP_RIGHT:
			f.x = viewWidth;
			f.y = 0;
			break;
		case STYLE_LOWER_RIGHT:
			f.x = viewWidth;
			f.y = viewHeight;
			break;
		default:
			break;
	}
	a.x = x;
	a.y = y;
	calcPointsXY(a,f);

	MyPoint touchPoint = new MyPoint(x,y);
	if(calcPointCX(touchPoint,f)<0){//若是c点x坐标小于0则从新测量a点坐标
		calcPointAByTouchPoint();
		calcPointsXY(a,f);
	}
	postInvalidate();
}

/** * 若是c点x坐标小于0,根据触摸点从新测量a点坐标 */
private void calcPointAByTouchPoint(){
	float w0 = viewWidth - c.x;

	float w1 = Math.abs(f.x - a.x);
	float w2 = viewWidth * w1 / w0;
	a.x = Math.abs(f.x - w2);

	float h1 = Math.abs(f.y - a.y);
	float h2 = w2 * h1 / w1;
	a.y = Math.abs(f.y - h2);
}
复制代码

效果如图


添加横向翻页效果

既然咱们实现的是仿真的翻页效果,翻页除了从上下两角翻,天然还能横向水平进行翻页。咱们先将View划分红上下左右中五个区域,如图

咱们根据触摸起始的位置所位于的区域,定义五种不一样的手势操做,其中上下(top,low)对应的是上下角进行翻页,左右(left,right)对应的横向水平翻页,中间(middle)则是为了之后做为呼出菜单而保留的区域。为了提升代码复用率和尽量小的改动,咱们实现横向翻页只需将a点的y坐标强制等于View的高度减1便可(固然你们也能够根据本身的须要从新计算横向翻页时的绘制区域,我这里就简单实现了),修改咱们的BookPageView

private String style;
public static final String STYLE_LEFT = "STYLE_LEFT";//点击左边区域
public static final String STYLE_RIGHT = "STYLE_RIGHT";//点击右边区域
public static final String STYLE_MIDDLE = "STYLE_MIDDLE";//点击中间区域
public static final String STYLE_TOP_RIGHT = "STYLE_TOP_RIGHT";//f点在右上角
public static final String STYLE_LOWER_RIGHT = "STYLE_LOWER_RIGHT";//f点在右下角

/** * 设置触摸点 * @param x * @param y * @param style */
public void setTouchPoint(float x, float y, String style){
	MyPoint touchPoint = new MyPoint();
	a.x = x;
	a.y = y;
	this.style = style;
	switch (style){
		case STYLE_TOP_RIGHT:
			f.x = viewWidth;
			f.y = 0;
			calcPointsXY(a,f);
			touchPoint = new MyPoint(x,y);
			if(calcPointCX(touchPoint,f)<0){//若是c点x坐标小于0则从新测量a点坐标
				calcPointAByTouchPoint();
				calcPointsXY(a,f);
			}
			postInvalidate();
			break;
		case STYLE_LEFT:
		case STYLE_RIGHT:
			a.y = viewHeight-1;
			f.x = viewWidth;
			f.y = viewHeight;
			calcPointsXY(a,f);
			postInvalidate();
			break;
		case STYLE_LOWER_RIGHT:
			f.x = viewWidth;
			f.y = viewHeight;
			calcPointsXY(a,f);
			touchPoint = new MyPoint(x,y);
			if(calcPointCX(touchPoint,f)<0){//若是c点x坐标小于0则从新测量a点坐标
				calcPointAByTouchPoint();
				calcPointsXY(a,f);
			}
			postInvalidate();
			break;
		default:
			break;
	}
}
复制代码

在Activity中监听触摸操做

public class PageActivity extends AppCompatActivity {
    private BookPageView bookPageView;
    private String style = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_page);

        bookPageView = (BookPageView) findViewById(R.id.view_book_page);
        bookPageView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                switch (event.getAction()){
                    case MotionEvent.ACTION_DOWN:
                        float x = event.getX();
                        float y = event.getY();
                        float width = bookPageView.getViewWidth();
                        float height = bookPageView.getViewHeight();
                        if(x<=width/3){//左
                            style = bookPageView.STYLE_LEFT;
// Toast.makeText(PageActivity.this,"点击了左部",Toast.LENGTH_SHORT).show();
                            bookPageView.setTouchPoint(x,y,style);

                        }else if(x>width/3 && y<=height/3){//上
                            style = bookPageView.STYLE_TOP_RIGHT;
// Toast.makeText(PageActivity.this,"点击了上部",Toast.LENGTH_SHORT).show();
                            bookPageView.setTouchPoint(x,y,style);

                        }else if(x>width*2/3 && y>height/3 && y<=height*2/3){//右
                            style = bookPageView.STYLE_RIGHT;
// Toast.makeText(PageActivity.this,"点击了右部",Toast.LENGTH_SHORT).show();
                            bookPageView.setTouchPoint(x,y,style);

                        }else if(x>width/3 && y>height*2/3){//下
                            style = bookPageView.STYLE_LOWER_RIGHT;
// Toast.makeText(PageActivity.this,"点击了下部",Toast.LENGTH_SHORT).show();
                            bookPageView.setTouchPoint(x,y,style);

                        }else if(x>width/3 && x<width*2/3 && y>height/3 && y<height*2/3){//中
                            style = bookPageView.STYLE_MIDDLE;
// Toast.makeText(PageActivity.this,"点击了中部",Toast.LENGTH_SHORT).show();
// bookPageView.setTouchPoint(x,y,bookPageView.STYLE_MIDDLE);
                        }
                        break;
                    case MotionEvent.ACTION_MOVE:
                        bookPageView.setTouchPoint(event.getX(),event.getY(),style);
                        break;
                    case MotionEvent.ACTION_UP:
                        bookPageView.setDefaultPath();
                        break;
                }
                return false;
            }
        });
    }
}
复制代码

效果如图


增长取消翻页的动画

android scroller类的使用

Android学习之 Scroller的介绍与使用

Android Scroller彻底解析,关于Scroller你所需知道的一切

Android -- Interpolator

android动画 之Interpolator类

由于咱们还没实现将书籍内容导入View中,因此咱们先来实现取消翻页的动画效果。这里咱们结合ScrollerInterpolator插值器,实现当咱们手指离开屏幕时,a点能自动滑落到右下角(右上角)的效果,有关ScrollerInterpolator方面的知识,我将相关博客连接贴出来了,你们能够相互对照着理解,我就不详细阐述了。修改BookPageView

private Scroller mScroller;

private void init(Context context, @Nullable AttributeSet attrs){
	//省略部分代码...
	mScroller = new Scroller(context,new LinearInterpolator());//以常量速率滑动便可
}

@Override
public void computeScroll() {
	if (mScroller.computeScrollOffset()) {
		float x = mScroller.getCurrX();
		float y = mScroller.getCurrY();
		
		if(style.equals(STYLE_TOP_RIGHT)){
			setTouchPoint(x,y,STYLE_TOP_RIGHT);
		}else {
			setTouchPoint(x,y,STYLE_LOWER_RIGHT);
		}
		if (mScroller.getFinalX() == x && mScroller.getFinalY() == y){
			setDefaultPath();//重置默认界面
		}
	}
}

/** * 取消翻页动画,计算滑动位置与时间 */
public void startCancelAnim(){
	int dx,dy;
	//让a滑动到f点所在位置,留出1像素是为了防止当a和f重叠时出现View闪烁的状况
	if(style.equals(STYLE_TOP_RIGHT)){
		dx = (int) (viewWidth-1-a.x);
		dy = (int) (1-a.y);
	}else {
		dx = (int) (viewWidth-1-a.x);
		dy = (int) (viewHeight-1-a.y);
	}
	mScroller.startScroll((int) a.x, (int) a.y, dx, dy, 400);
}
复制代码

在Activity中监听手指抬起操做

case MotionEvent.ACTION_UP:
	bookPageView.startCancelAnim();
	break;
复制代码

效果如图

至此本篇教程就告一段落了,这期比较短,主要是对上期的补充。下期会实现书籍内容填充或者绘制阴影,看状况吧๑乛◡乛๑。若是你们看了感受还不错麻烦点个赞,大家的支持是我最大的动力~