自定義View實(shí)戰(zhàn)篇(二)實(shí)現(xiàn)小說翻頁二 基本原理

本文實(shí)現(xiàn)小說翻頁的基本原理
自定義View實(shí)戰(zhàn)篇(二)實(shí)現(xiàn)小說翻頁二實(shí)現(xiàn)翻頁動畫窃诉、陰影杨耙、內(nèi)容

一、簡介

首先感謝hmg25Android 實(shí)現(xiàn)書籍翻頁效果----原理篇飘痛,本文參考其實(shí)現(xiàn)珊膜,旨在鞏固這方面的知識,以及為自己后面的實(shí)戰(zhàn)做準(zhǔn)備宣脉。

研讀了實(shí)戰(zhàn)上面的原理篇之后车柠,我們可以知道實(shí)現(xiàn)翻頁效果,其實(shí)是根據(jù)一些動態(tài)點(diǎn)進(jìn)行計算塑猖,然后進(jìn)行剪切竹祷,最后繪制在畫布上,下面依次寫出各個點(diǎn)的計算方法羊苟。

首先塑陵,我們將綠色部分稱作A區(qū)域、藍(lán)色為B區(qū)域蜡励、黃色為C區(qū)域猿妈。

  • a:觸摸點(diǎn),在onTouchonTouchEvent()中獲取X巍虫、Y坐標(biāo)。

  • f:即view的大小鳍刷,通過onMeasuer()獲取View的寬高占遥。

  • ggaf的中點(diǎn),根據(jù)數(shù)學(xué)公式可得:

    g.x=(a.x+f.x)/2;

    g.y=(a.y+f.y)/2;

  • e:根據(jù)相似三角形egmggm可知输瓜,對應(yīng)邊成比例可得:

    e.x = g.x - (f.y - g.y) * ((f.y - g.y) / (f.x - g.x));

    e.y = f.y;

  • h:同理瓦胎,根據(jù)相似三角形egffgh可得:

    h.x = f.x;
    h.y = g.y - (f.x - g.x) * (f.x - g.x) / (f.y - g.y);

  • c:設(shè)nag中點(diǎn),同理尤揣,根據(jù)相似三角形fgefnc搔啊,且比例為1:2,可得

    c.x = e.x - (f.x - e.x) / 2;
    c.y = f.y;

  • c:設(shè)nag中點(diǎn)北戏,同理负芋,根據(jù)相似三角形fghfnj,且比例為1:2嗜愈,可得

    j.x = f.x;
    j.y = h.y - (f.y - h.y) / 2;

  • b&k:根據(jù)相似三角形abkaeh旧蛾,且比例為1:2,可得
    b.x = (a.x+e.x)/2;

    b.y = (a.y+e.y)/2;

    k.x = (a.x+h.x)/2;

    k.y = (a.y+h.y)/2;

  • pcb的中點(diǎn)蠕嫁,dpe的中點(diǎn)锨天,所以

    d.x=((c.x+b.x)/2+e.x)/2
    d.y=((c.y+b.y)/2+e.y)/2

  • rkj的中點(diǎn),dhr的中點(diǎn)剃毒,所以
    i.x=((k.x+j.x)/2+h.x)/2
    i.y=((k.y+j.y)/2+h.y)/2

二病袄、實(shí)現(xiàn)仿真翻頁

1搂赋、基本實(shí)現(xiàn)

(1)首先,我們定義一個類保存各個點(diǎn)的坐標(biāo)益缠,然后由觸摸點(diǎn)a和已知的點(diǎn)f獲取其他坐標(biāo)脑奠,由此我們通過不斷獲取觸摸點(diǎn)然后配合f點(diǎn)坐標(biāo)對各個點(diǎn)進(jìn)行更新。

public class Point {
    public float x;
    public float y;
}
/**
 * 計算各個點(diǎn)的坐標(biāo)
 * @param a a點(diǎn)的坐標(biāo)
 * @param f f點(diǎn)的坐標(biāo)
 */
void calculationPoint(Point a, Point f) {
    g.x = (a.x + f.x) / 2;
    g.y = (a.y + f.y) / 2;

    e.x = g.x - (f.y - g.y) * (f.y - g.y) / (f.x - g.x);
    e.y = f.y;

    h.x = f.x;
    h.y = g.y - (f.x - g.x) * ((f.x - g.x) / (f.y - g.y));

    c.x = e.x - (f.x - e.x) / 2;
    c.y = f.y;

    j.x = f.x;
    j.y = h.y - (f.y - h.y) / 2;

    b.x = (a.x + e.x) / 2;
    b.y = (a.y + e.y) / 2;

    k.x = (a.x + h.x) / 2;
    k.y = (a.y + h.y) / 2;

    d.x = ((c.x + b.x) / 2 + e.x) / 2;
    d.y = ((c.y + b.y) / 2 + e.y) / 2;

    i.x = ((k.x + j.x) / 2 + h.x) / 2;
    i.y = ((k.y + j.y) / 2 + h.y) / 2;
}

(2)獲取各個點(diǎn)坐標(biāo)后左刽,我們需要對A捺信、BC區(qū)域得到去Path路徑

A區(qū)域計算方法:

左下角A區(qū)域我們可以從0.0出發(fā)欠痴,畫直線至左下角迄靠,然后畫直線到C點(diǎn),然后由二次貝塞爾曲線到b點(diǎn)喇辽,然后畫直線a點(diǎn)掌挚,在畫直線到k點(diǎn),再由二次貝塞爾曲線到j點(diǎn)菩咨,然后畫直線到右上角最后閉合到0.0點(diǎn)吠式。

mPathA.reset();
mPathA.lineTo(0, height);
mPathA.lineTo(c.x, c.y);
mPathA.quadTo(e.x, e.y, b.x, b.y);
mPathA.lineTo(a.x, a.y);
mPathA.lineTo(k.x, k.y);
mPathA.quadTo(h.x, h.y, j.x, j.y);
mPathA.lineTo(width, 0);
mPathA.close();
return mPathA;

B區(qū)域計算方法:我們只需獲取整個頁面的path即可,原因是因?yàn)槲覀兎摰奈恢每赡苁侨我庖粋€角抽米,但是如果我們將區(qū)域B也跟隨F點(diǎn)去判斷的話特占,那代碼將不夠靈了。

mPathB.reset();
mPathB.lineTo(0, height);
mPathB.lineTo(width, height);
mPathB.lineTo(width, 0);
mPathB.close();//閉合區(qū)域
return mPathB;

C區(qū)域計算方法:C區(qū)域與A基本相同

mPathC.reset();
mPathC.moveTo(i.x, i.y);
mPathC.lineTo(d.x, d.y);
mPathC.lineTo(b.x, b.y);
mPathC.lineTo(a.x, a.y);
mPathC.lineTo(k.x, k.y);
mPathC.close();//閉合區(qū)域
return mPathC;

(3)對A云茸、B是目、C根據(jù) Path進(jìn)行裁切,裁切我們要用到CanvansclipPath方法:

clipPath由兩個構(gòu)造方法clipPath(Path path)标捺、clipPath(Path path, Region.Op op)

  • op:

    DIFFRENCE是第一次不同于第二次的部分顯示A-B
    REPLAC是顯示第二次的B
    REVERSE_DIFFRENCE是第二次不同于第一次的部分顯示
    INTERSECT是交集顯示
    UNION是全部顯示A+B
    XOR是補(bǔ)集(全集減去交集剩余部分)顯示

A區(qū)域安裝Path直接剪切

canvas.clipPath(getPathA());

B區(qū)域懊纳,先剪切A,在剪切C,然后我們設(shè)置UNION即剪切A+C的區(qū)域亡容,然后設(shè)置REVERSE_DIFFERENCE嗤疯,剪切除A+C的部分,即B的部分闺兢。

canvas.clipPath(getPathA());
canvas.clipPath(getPathC(), Region.Op.UNION);
canvas.clipPath(getPathB(), Region.Op.REVERSE_DIFFERENCE);

C區(qū)域:這里需要思考茂缚,為什么我們不直接剪切C而是先剪切A在剪切C且減去與區(qū)域A的交集部分

canvas.clipPath(getPathA());
canvas.clipPath(getPathC(), Region.Op.REVERSE_DIFFERENCE);

當(dāng)然到這里我們已經(jīng)可以將代碼組合一下,實(shí)現(xiàn)最簡單的翻頁了列敲。

(3)通過觸摸事件阱佛,實(shí)現(xiàn)滑動,完整代碼如下戴而。

/**
 * @author Active_Loser
 * @date 2018/11/18
 * Content: 自定義PageView
 * A: 表示當(dāng)前頁面
 * B: 表示上一頁或下一頁的頁面
 * C: 表示翻起的頁面凑术,即當(dāng)前頁的背面
 */
public class PageView extends View {

    private Path mPathA;
    private Path mPathB;
    private Path mPathC;
    private Bitmap mBitmapA;
    private Bitmap mBitmapB;
    private Bitmap mBitmapC;

    /**
     * 測量出view的寬高
     */
    private int width, height;
    private Point a, f, g, e, h, c, j, b, k, d, i;

    public PageView(Context context) {
        this(context, null);
    }

    public PageView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public PageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        a = new Point();
        f = new Point();
        g = new Point();
        e = new Point();
        h = new Point();
        c = new Point();
        j = new Point();
        b = new Point();
        k = new Point();
        d = new Point();
        i = new Point();

        mPathA = new Path();
        mPathB = new Path();
        mPathC = new Path();
    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        width = getDefaultSize(600, widthMeasureSpec);
        height = getDefaultSize(1000, heightMeasureSpec);
        setMeasuredDimension(width, height);

        f.x = width;
        f.y = height;
        mBitmapA = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        mBitmapB = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        mBitmapC = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);

        Canvas mCanvasA = new Canvas(mBitmapA);
        mCanvasA.drawColor(Color.GREEN);
        Canvas mCanvasB = new Canvas(mBitmapB);
        mCanvasB.drawColor(Color.YELLOW);
        Canvas mCanvasC = new Canvas(mBitmapC);
        mCanvasC.drawColor(Color.BLUE);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        float x = event.getX();
        float y = event.getY();
        a.x = x;
        a.y = y;
        calculationPoint(a, f);
        postInvalidate();
        return true;
    }
  
    @Override
    protected void onDraw(Canvas canvas) {
        drawA(canvas);
        drawC(canvas);
        drawB(canvas);
    }

    /**
     * 剪切A區(qū)域
     */
    private void drawA(Canvas canvas) {
        canvas.save();
        canvas.clipPath(getPathA());
        canvas.drawBitmap(mBitmapA, 0, 0, null);
        canvas.restore();
    }

    /**
     * 剪切C區(qū)域
     */
    private void drawC(Canvas canvas) {
        canvas.save();
        canvas.clipPath(getPathA());
        canvas.clipPath(getPathC(), Region.Op.REVERSE_DIFFERENCE);
        canvas.drawBitmap(mBitmapC, 0, 0, null);
        canvas.restore();
    }

    /**
     * 剪切B區(qū)域
     */
    private void drawB(Canvas canvas) {
        canvas.save();
        canvas.clipPath(getPathA());
        canvas.clipPath(getPathC(), Region.Op.UNION);
        canvas.clipPath(getPathB(), Region.Op.REVERSE_DIFFERENCE);
        canvas.drawBitmap(mBitmapB, 0, 0, null);
        canvas.restore();
    }
    
     /**
     * 獲取區(qū)域A的path
     */
    private Path getPathA() {
        mPathA.reset();
        mPathA.lineTo(0, height);
        mPathA.lineTo(c.x, c.y);
        mPathA.quadTo(e.x, e.y, b.x, b.y);
        mPathA.lineTo(a.x, a.y);
        mPathA.lineTo(k.x, k.y);
        mPathA.quadTo(h.x, h.y, j.x, j.y);
        mPathA.lineTo(width, 0);
        mPathA.close();
        return mPathA;
    }

    /**
     * 獲取區(qū)域C的path
     */
    private Path getPathC() {
        mPathC.reset();
        mPathC.moveTo(i.x, i.y);
        mPathC.lineTo(d.x, d.y);
        mPathC.lineTo(b.x, b.y);
        mPathC.lineTo(a.x, a.y);
        mPathC.lineTo(k.x, k.y);
        mPathC.close();//閉合區(qū)域
        return mPathC;
    }

    /**
     * 獲取區(qū)域B的path
     */
    private Path getPathB() {
        mPathB.reset();
        mPathB.lineTo(0, height);
        mPathB.lineTo(width, height);
        mPathB.lineTo(width, 0);
        mPathB.close();//閉合區(qū)域
        return mPathB;
    }
   
    /**
     * 計算各個點(diǎn)的坐標(biāo)
     *
     * @param a a點(diǎn)的坐標(biāo)
     * @param f f點(diǎn)的坐標(biāo)
     */
    void calculationPoint(Point a, Point f) {
        g.x = (a.x + f.x) / 2;
        g.y = (a.y + f.y) / 2;

        e.x = g.x - (f.y - g.y) * (f.y - g.y) / (f.x - g.x);
        e.y = f.y;

        h.x = f.x;
        h.y = g.y - (f.x - g.x) * ((f.x - g.x) / (f.y - g.y));

        c.x = e.x - (f.x - e.x) / 2;
        c.y = f.y;

        j.x = f.x;
        j.y = h.y - (f.y - h.y) / 2;

        b.x = (a.x + e.x) / 2;
        b.y = (a.y + e.y) / 2;

        k.x = (a.x + h.x) / 2;
        k.y = (a.y + h.y) / 2;

        d.x = ((c.x + b.x) / 2 + e.x) / 2;
        d.y = ((c.y + b.y) / 2 + e.y) / 2;

        i.x = ((k.x + j.x) / 2 + h.x) / 2;
        i.y = ((k.y + j.y) / 2 + h.y) / 2;
    }
}

2、限制翻頁距離

我們觀察上面的翻頁動畫最后時所意,淮逊,我們的書籍隨時翻動催首,但是左側(cè)最后也隨之翻動起來,這樣明顯不符合翻頁的規(guī)則泄鹏,我們對C點(diǎn)進(jìn)行限制郎任。

思考,若我們的C點(diǎn)為負(fù)數(shù)备籽,即左側(cè)也被翻起的時候传黄,我們需要將C點(diǎn)一直放在零界點(diǎn)的位置勾怒,而j點(diǎn)繼續(xù)向上移動糖驴,因此我們使用相似圖形的原理淆党,梯形camf和c1a1m1f1相似,重新計算a的坐標(biāo)(a1)珠闰。

private void calculationAByYouch(){
    float cf = width - c.x;

    float pf = Math.abs(f.x - a.x);
    float p1f = width * pf / cf;
    a.x = Math.abs(f.x - p1f);

    float h1 = Math.abs(f.y - a.y);
    float a1p1 = h1 *  pf / cf;
    a.y = Math.abs(f.y - a1p1);
}

 @Override
    public boolean onTouchEvent(MotionEvent event) {
        float x = event.getX();
        float y = event.getY();
        a.x = x;
        a.y = y;
        calculationPoint(a, f);
        if (c.x<0){
            calculationAByYouch();
            calculationPoint(a, f);
        }
        postInvalidate();
         return true;
    }

最后編輯于
?著作權(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
  • 正文 我和宋清朗相戀三年卵皂,在試婚紗的時候發(fā)現(xiàn)自己被綠了秩铆。 大學(xué)時的朋友給我發(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)容