版權宣告:本文為博主原創文章,未經博主允許不得轉載
系列教程:Android開發之從零開始系列
原始碼:AnliaLee/BookPage,歡迎star
大家要是看到有錯誤的地方或者有啥好的建議,歡迎留言評論
前言:在上篇Android自定義View——從零開始實現書籍翻頁效果(一)部落格中,我們實現了 基本的上下翻頁效果與 右側最大翻頁距離的限制,這期我們要將這個view的翻頁效果以及動畫補齊
本篇只著重於思路和實現步驟,裡面用到的一些知識原理不會非常細地拿來講,如果有不清楚的api或方法可以在網上搜下相應的資料,肯定有大神講得非常清楚的,我這就不獻醜了。本著認真負責的精神我會把相關知識的博文連結也貼出來(其實就是懶不想寫那麼多哈哈),大家可以自行傳送。為了照顧第一次閱讀系列部落格的小夥伴,本篇會出現一些在之前系列部落格就講過的內容,看過的童鞋自行跳過該段即可
國際慣例,先上效果圖,本次主要補全了翻頁效果以及增加取消翻頁的動畫
完善右側最大翻頁距離的限制
開講之前,我先把標識點的位置圖貼出來讓大家回顧一下
在上篇部落格中我們講了如何限制翻頁的最大距離,也就是c點的x座標不能小於0,雖然目的達到了,但是使用者體驗並不好,可以很明顯地觀察到當c點x座標處於臨界值時,翻頁會靜止不動,如果此時觸控點大範圍移動後,會出現翻頁“瞬移”,造成一種卡頓的感覺,如下圖
要消除這種“瞬移”的現象,我們要在c點x座標小於0的情況下,讓c點強制處於臨界位置(左下角),然後翻頁頁角繼續跟隨觸控點移動的方向移動,要做到這一點得用到一些相似三角形的數學知識,重新計算出a點的位置繪製View,先來看下實現的原理(請無視我的渣畫工╮(╯▽╰)╭ )
圖中我們將觸控點標為a1,與a1對應的c點標為c1,此時c1的x座標是小於0。而a2是我們重新計算得到的a點,也就是最後進行繪製的a點,其對應的c點標為c2,c2位於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;
}
});
}
}
複製程式碼
效果如圖
增加取消翻頁的動畫
因為我們還沒實現將書籍內容匯入View中,所以我們先來實現取消翻頁的動畫效果。這裡我們結合Scroller和Interpolator插值器,實現當我們手指離開螢幕時,a點能自動滑落到右下角(右上角)的效果,有關Scroller和Interpolator方面的知識,我將相關部落格連結貼出來了,大家可以相互對照著理解,我就不詳細闡述了。修改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;
複製程式碼
效果如圖
至此本篇教程就告一段落了,這期比較短,主要是對上期的補充。下期會實現書籍內容填充或者繪製陰影,看情況吧๑乛◡乛๑。如果大家看了感覺還不錯麻煩點個贊,你們的支援是我最大的動力~