Android自定義View——從零開始實(shí)現(xiàn)書籍翻頁效果(二)

版權(quán)聲明:本文為博主原創(chuàng)文章跺株,未經(jīng)博主允許不得轉(zhuǎn)載秧荆。
系列教程:Android開發(fā)之從零開始系列
源碼:github.com/AnliaLee/BookPage,歡迎star

大家要是看到有錯誤的地方或者有啥好的建議县踢,歡迎留言評論

前言:在上篇Android自定義View——從零開始實(shí)現(xiàn)書籍翻頁效果(一)博客中瘸彤,我們實(shí)現(xiàn)了 基本的上下翻頁效果右側(cè)最大翻頁距離的限制,這期我們要將這個view的翻頁效果以及動畫補(bǔ)齊

本篇只著重于思路和實(shí)現(xiàn)步驟徊哑,里面用到的一些知識原理不會非常細(xì)地拿來講袜刷,如果有不清楚的api或方法可以在網(wǎng)上搜下相應(yīng)的資料,肯定有大神講得非常清楚的莺丑,我這就不獻(xiàn)丑了著蟹。本著認(rèn)真負(fù)責(zé)的精神我會把相關(guān)知識的博文鏈接也貼出來(其實(shí)就是懶不想寫那么多哈哈),大家可以自行傳送梢莽。為了照顧第一次閱讀系列博客的小伙伴萧豆,本篇會出現(xiàn)一些在之前系列博客就講過的內(nèi)容,看過的童鞋自行跳過該段即可

國際慣例昏名,先上效果圖炕横,本次主要補(bǔ)全了翻頁效果以及增加取消翻頁的動畫

目錄
  • 完善右側(cè)最大翻頁距離的限制
  • 添加橫向翻頁效果
  • 增加取消翻頁的動畫

完善右側(cè)最大翻頁距離的限制

開講之前,我先把標(biāo)識點(diǎn)的位置圖貼出來讓大家回顧一下

在上篇博客中我們講了如何限制翻頁的最大距離葡粒,也就是c點(diǎn)的x坐標(biāo)不能小于0份殿,雖然目的達(dá)到了膜钓,但是用戶體驗(yàn)并不好,可以很明顯地觀察到當(dāng)c點(diǎn)x坐標(biāo)處于臨界值時(shí)卿嘲,翻頁會靜止不動颂斜,如果此時(shí)觸摸點(diǎn)大范圍移動后,會出現(xiàn)翻頁“瞬移”拾枣,造成一種卡頓的感覺沃疮,如下圖

要消除這種“瞬移”的現(xiàn)象,我們要在c點(diǎn)x坐標(biāo)小于0的情況下梅肤,讓c點(diǎn)強(qiáng)制處于臨界位置(左下角),然后翻頁頁角繼續(xù)跟隨觸摸點(diǎn)移動的方向移動司蔬,要做到這一點(diǎn)得用到一些相似三角形的數(shù)學(xué)知識,重新計(jì)算出a點(diǎn)的位置繪制View姨蝴,先來看下實(shí)現(xiàn)的原理(請無視我的渣畫工╮(╯▽╰)╭ )

圖中我們將觸摸點(diǎn)標(biāo)為a1俊啼,與a1對應(yīng)的c點(diǎn)標(biāo)為c1,此時(shí)c1的x坐標(biāo)是小于0左医。而a2是我們重新計(jì)算得到的a點(diǎn)授帕,也就是最后進(jìn)行繪制的a點(diǎn),其對應(yīng)的c點(diǎn)標(biāo)為c2浮梢,c2位于View的左下角(x坐標(biāo)為0)跛十,容易觀察到直角三角形a1 c1 m1直角三角形a2 c2 m2相似,此時(shí)f點(diǎn)位于View的右下角(在右上角同理)秕硝,因此我們可以通過相應(yīng)的公式計(jì)算得到a2的坐標(biāo)為(f.x-w2芥映,f.y-h2),得到計(jì)算a2的方法后远豺,我們修改原來的BookPageView

/**
 * 設(shè)置觸摸點(diǎn)
 * @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點(diǎn)x坐標(biāo)小于0則重新測量a點(diǎn)坐標(biāo)
        calcPointAByTouchPoint();
        calcPointsXY(a,f);
    }
    postInvalidate();
}

/**
 * 如果c點(diǎn)x坐標(biāo)小于0,根據(jù)觸摸點(diǎn)重新測量a點(diǎn)坐標(biāo)
 */
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);
}

效果如圖


添加橫向翻頁效果

既然我們實(shí)現(xiàn)的是仿真的翻頁效果屏轰,翻頁除了從上下兩角翻,自然還能橫向水平進(jìn)行翻頁憋飞。我們先將View劃分成上下左右中五個區(qū)域霎苗,如圖

我們根據(jù)觸摸起始的位置所位于的區(qū)域,定義五種不同的手勢操作榛做,其中上下(top,low)對應(yīng)的是上下角進(jìn)行翻頁唁盏,左右(left,right)對應(yīng)的橫向水平翻頁,中間(middle)則是為了以后作為呼出菜單而保留的區(qū)域检眯。為了提高代碼復(fù)用率和盡可能小的改動厘擂,我們實(shí)現(xiàn)橫向翻頁只需將a點(diǎn)的y坐標(biāo)強(qiáng)制等于View的高度減1即可(當(dāng)然大家也可以根據(jù)自己的需要重新計(jì)算橫向翻頁時(shí)的繪制區(qū)域,我這里就簡單實(shí)現(xiàn)了)锰瘸,修改我們的BookPageView

private String style;
public static final String STYLE_LEFT = "STYLE_LEFT";//點(diǎn)擊左邊區(qū)域
public static final String STYLE_RIGHT = "STYLE_RIGHT";//點(diǎn)擊右邊區(qū)域
public static final String STYLE_MIDDLE = "STYLE_MIDDLE";//點(diǎn)擊中間區(qū)域
public static final String STYLE_TOP_RIGHT = "STYLE_TOP_RIGHT";//f點(diǎn)在右上角
public static final String STYLE_LOWER_RIGHT = "STYLE_LOWER_RIGHT";//f點(diǎn)在右下角

/**
 * 設(shè)置觸摸點(diǎn)
 * @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點(diǎn)x坐標(biāo)小于0則重新測量a點(diǎn)坐標(biāo)
                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點(diǎn)x坐標(biāo)小于0則重新測量a點(diǎn)坐標(biāo)
                calcPointAByTouchPoint();
                calcPointsXY(a,f);
            }
            postInvalidate();
            break;
        default:
            break;
    }
}

在Activity中監(jiān)聽觸摸操作

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,"點(diǎn)擊了左部",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,"點(diǎn)擊了上部",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,"點(diǎn)擊了右部",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,"點(diǎn)擊了下部",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,"點(diǎn)擊了中部",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學(xué)習(xí)之 Scroller的介紹與使用
Android Scroller完全解析刽严,關(guān)于Scroller你所需知道的一切
Android -- Interpolator
android動畫 之Interpolator類

因?yàn)槲覀冞€沒實(shí)現(xiàn)將書籍內(nèi)容導(dǎo)入View中,所以我們先來實(shí)現(xiàn)取消翻頁的動畫效果避凝。這里我們結(jié)合ScrollerInterpolator插值器舞萄,實(shí)現(xiàn)當(dāng)我們手指離開屏幕時(shí)眨补,a點(diǎn)能自動滑落到右下角(右上角)的效果,有關(guān)ScrollerInterpolator方面的知識倒脓,我將相關(guān)博客鏈接貼出來了撑螺,大家可以相互對照著理解,我就不詳細(xì)闡述了崎弃。修改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();//重置默認(rèn)界面
        }
    }
}

/**
 * 取消翻頁動畫,計(jì)算滑動位置與時(shí)間
 */
public void startCancelAnim(){
    int dx,dy;
    //讓a滑動到f點(diǎn)所在位置甘晤,留出1像素是為了防止當(dāng)a和f重疊時(shí)出現(xiàn)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中監(jiān)聽手指抬起操作

case MotionEvent.ACTION_UP:
    bookPageView.startCancelAnim();
    break;

效果如圖

至此本篇教程就告一段落了,這期比較短饲做,主要是對上期的補(bǔ)充线婚。下期會實(shí)現(xiàn)書籍內(nèi)容填充或者繪制陰影,看情況吧?乛?乛?盆均。如果大家看了感覺還不錯麻煩點(diǎn)個贊塞弊,你們的支持是我最大的動力~


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市缀踪,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌虹脯,老刑警劉巖驴娃,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異循集,居然都是意外死亡唇敞,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進(jìn)店門咒彤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來疆柔,“玉大人,你說我怎么就攤上這事镶柱】醯担” “怎么了?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵歇拆,是天一觀的道長鞋屈。 經(jīng)常有香客問我,道長故觅,這世上最難降的妖魔是什么厂庇? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮输吏,結(jié)果婚禮上权旷,老公的妹妹穿的比我還像新娘。我一直安慰自己贯溅,他們只是感情好拄氯,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布躲查。 她就那樣靜靜地躺著,像睡著了一般坤邪。 火紅的嫁衣襯著肌膚如雪熙含。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天艇纺,我揣著相機(jī)與錄音怎静,去河邊找鬼。 笑死黔衡,一個胖子當(dāng)著我的面吹牛蚓聘,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播盟劫,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼夜牡,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了侣签?” 一聲冷哼從身側(cè)響起塘装,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎影所,沒想到半個月后蹦肴,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡猴娩,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年阴幌,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片卷中。...
    茶點(diǎn)故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡矛双,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蟆豫,到底是詐尸還是另有隱情议忽,我是刑警寧澤,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布十减,位于F島的核電站徙瓶,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏嫉称。R本人自食惡果不足惜侦镇,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望织阅。 院中可真熱鬧壳繁,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至渣触,卻和暖如春羡棵,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背嗅钻。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工皂冰, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人养篓。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓秃流,卻偏偏與公主長得像,于是被迫代替她去往敵國和親柳弄。 傳聞我的和親對象是個殘疾皇子舶胀,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內(nèi)容