Android側滑粘稠效果的實現(xiàn)

*本篇文章已授權微信公眾號 guolin_blog (郭霖)獨家發(fā)布 *

之前在UI中國上看到一個側滑效果堡牡,覺得還不錯,于是就想實現(xiàn)一下锈嫩。
UI效果是這樣的:http://www.ui.cn/detail/198520.html

我自己實現(xiàn)的效果是這樣的:


cehua.gif

效果并沒有完全一致胁附,UI效果里當兩部分重合后有一種像水滴重合一樣的效果榴啸,我并不知道用代碼如何實現(xiàn)(如果大家知道請告訴我呜象。膳凝。。)恭陡,所以我這里只是簡單的做了一個慢慢出現(xiàn)的效果蹬音。

好了,那我們就來看看這個效果是怎么實現(xiàn)的休玩。

首先

這是一個自定義View著淆。
效果分為兩個部分,一個是背景的紅色拴疤,一個是白色的叉叉牧抽。

背景的紅色

當看到紅色的效果的時候,我就想到了貝塞爾曲線遥赚。貝塞爾曲線是個神奇的東西,幾乎所有神奇的曲線效果都可以用貝塞爾曲線來做阐肤。為了讓這篇文章保持簡潔凫佛,所以我就不介紹貝塞爾曲線了,如果有不了解的人孕惜,可以去這里看看愧薛。

到這里,我就假設你們都已經(jīng)知道怎么畫貝塞爾曲線啦衫画。
我想你們也猜到了毫炉,紅色效果其實就是用的三階貝塞爾曲線。

紅色效果.png

這個圖畫的就是紅色效果的示意圖(為了介紹所以將圖畫成了半圓削罩,實際效果里瞄勾,圓是扁扁的)。為了便于計算我將它設置為了一個正方形弥激,圖中的x2,x3,x5,x6都是中點进陡。我們用Path.cubicTo()方法將x1到x7這七個點連起來之后就可以得一個半圓形了。
我們整個效果微服,其實就是控制這幾個點而已趾疚。

背景紅色的運動過程

在這里,我會直接給出代碼來講解。
為了便于計算糙麦,我們先將控件的寬高設為相等辛孵,讓它變成一個正方形:

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec,widthMeasureSpec);
    }

然后就是繪制背景紅色了,其實這個紅色有上下兩個赡磅,但是兩個其實是一樣的魄缚,所以我就只介紹上半部分。

        pathUp.reset();
        pathUp.moveTo(0,0);
        pathUp.cubicTo(x2,y26,0,y345,radius,y345);
        pathUp.cubicTo(radius*2,y345,x6,y26,radius*2,0);
        pathUp.lineTo(0,0);
        canvas.drawPath(pathUp,paint);

這里的過程就是從x1點開始連接一直連接到x7仆邓,最后在連到x1械巡。
其中x2表示x2點的x坐標整葡,y26表示x2點和x6點的y坐標,其余類同(取名字真的好累)。
radius表示圓的半徑登刺,也就是正方形邊長的一半。
代碼里嗤朴,x3點和x5點的橫坐標被我設置為和x1,x7一樣剥懒,這樣可以讓圓扁一點。
我們需要變化的點匿乃,其實就是x2,x6,y26,y345這四個點而已桩皿。
y345,y26需要上下移動幢炸,來達到變小變大的效果泄隔。
至于x2,x6則是需要向外擴大,不然最后y345就算捅穿地表也不能把兩邊填滿宛徊。
現(xiàn)在我們知道了需要變化的點的軌跡佛嬉,那我們怎么控制它們呢?
我在View里寫了一個方法:

public void controllAnimation(int progress,float max)

這個方法接收兩個參數(shù)闸天,第一個是當前值暖呕,第二個是最大的值。通過 當前值/最大值 我們就可以獲取一個百分比的進度值苞氮。通過這個進度值我們就可以計算出當前點的位置:

    public void controllAnimation(int progress,float max){
        double fraction = progress/max;

        //這里根據(jù)進度改變湾揽,慢慢的變化
        y345 = (float) (radius*2*(fraction));

        if(y345<radius){
            x2 = 0;
            x6 = radius*2-x2;
        }else {
            x2 = (float) (-radius*2*((fraction-0.5)/fraction));
            x6 = radius*2-x2;
        }

        y26 = (float) (radius*fraction);
        invalidate();
    }

現(xiàn)在這個我用了一個seekBar,等到將其放進真的比如RecyclerView的側滑刪除時笼吟,只要將滑動距離作為progress參數(shù)傳進去库物,滑動最大值作為max傳進去就可以了。

白色叉叉

叉叉.png

叉叉就簡單多了贷帮,圖上的每個點都是中點艳狐。
叉叉也是分上半部和下半部,我們這里就只講講上半部分皿桑,下半部分一樣的毫目。

            //這里畫那個叉叉
            //這是上半部分
            pathCha.reset();
            pathCha.moveTo((halfRadius*3)/2,radius);
            pathCha.lineTo(halfRadius,chaY1);
            pathCha.lineTo((halfRadius*3)/2,chaY2);
            pathCha.lineTo(radius,chaY1);
            pathCha.lineTo((radius*2)-((halfRadius*3)/2),chaY2);
            pathCha.lineTo(halfRadius*3,chaY1);
            pathCha.lineTo((radius*2)-((halfRadius*3)/2),radius);
            pathCha.lineTo((halfRadius*3)/2,radius);

這里radius表示半徑蔬啡,halfRadius表示半徑的一半。
chaY1等于(radius*3)/4
chaY2等于 radius/2
chaY1表示x1,x3,x5點的y坐標镀虐,chaY2表示x2,x4點的y坐標箱蟆。
我們需要改變的就是chaY1和chaY2的值,來達到一種叉叉慢慢出現(xiàn)的感覺刮便。
代碼如下:

        if(y345<radius){
            //一些代碼
        }else {
            ...
            //慢慢的畫出叉
            chaY1 = (float) (radius-(chaLength*2*((fraction-0.5)/fraction)));
            chaY2 = (float) (radius-(halfRadius*2*((fraction-0.5)/fraction)));
        }

我們的叉叉是在上下兩個紅色接觸在一起的時候才繪制的空猜,也就是進度為一半的時候。

這樣就把這個View說完了恨旱,View的代碼也不是很長辈毯,我就直接貼出來吧:

public class CeHuaView extends View {

    Paint paint,paintCha;
    Path pathUp,pathDown,pathCha,pathCha2;

    int width;

    float radius;
    float halfRadius;

    //這里取名字有點隨便,因為不知道怎么取搜贤,可以看我博客里的圖谆沃,應該就能知道意思了
    //這里y表示上半部分,yy表示下半部分仪芒,x同理
    float y345,yy345;
    float y26,yy26;
    float x2,x6;

    float chaLength;
    float chaY1,chaY2;

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

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

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

    private void init() {
        pathUp = new Path();
        pathDown = new Path();
        pathCha = new Path();
        pathCha2 = new Path();

        paint = new Paint();
        paint.setColor(Color.parseColor("#ff5777"));
        paint.setAntiAlias(true);
        paint.setStyle(Paint.Style.FILL);

        paintCha = new Paint();
        paintCha.setColor(Color.parseColor("#ffffff"));
        paintCha.setAntiAlias(true);
        paintCha.setStyle(Paint.Style.FILL);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec,widthMeasureSpec);
        width = MeasureSpec.getSize(widthMeasureSpec);

        radius = width/2;
        halfRadius = width/4;
        y345 = radius;
        yy345 = radius;

        y26 = halfRadius;
        yy26 = halfRadius*3;

        x2 = 0;
        x6 = radius*2;

        chaLength = radius/4;
        chaY1 = chaLength*3;
        chaY2 = chaLength*2;
    }

    public void controllAnimation(int progress,float max){
        double fraction = progress/max;

        //這里根據(jù)進度改變唁影,慢慢的變化
        y345 = (float) (radius*2*(fraction));
        yy345 = radius*2-y345;

        if(y345<radius){
            x2 = 0;
            x6 = radius*2-x2;
        }else {
            x2 = (float) (-radius*2*((fraction-0.5)/fraction));
            x6 = radius*2-x2;

            //慢慢的畫出叉
            chaY1 = (float) (radius-(chaLength*2*((fraction-0.5)/fraction)));
            chaY2 = (float) (radius-(halfRadius*2*((fraction-0.5)/fraction)));
        }

        y26 = (float) (radius*fraction);
        yy26 = (float) ((radius*2)-(radius*fraction));
        invalidate();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //這里畫粘稠的效果
        //這是上半部分
        pathUp.reset();
        pathUp.moveTo(0,0);
        pathUp.cubicTo(x2,y26,0,y345,radius,y345);
        pathUp.cubicTo(radius*2,y345,x6,y26,radius*2,0);
        pathUp.lineTo(0,0);
        canvas.drawPath(pathUp,paint);

        //這里畫粘稠的效果
        //這是下半部分
        pathDown.reset();
        pathDown.moveTo(0,radius*2);
        pathDown.cubicTo(x2,yy26,0,yy345,radius,yy345);
        pathDown.cubicTo(radius*2,yy345,x6,yy26,radius*2,radius*2);
        pathDown.lineTo(0,radius*2);
        canvas.drawPath(pathDown,paint);

        if(y345>radius){
            //這里畫那個叉叉
            //這是上半部分
            pathCha.reset();
            pathCha.moveTo((halfRadius*3)/2,radius);
            pathCha.lineTo(halfRadius,chaY1);
            pathCha.lineTo((halfRadius*3)/2,chaY2);
            pathCha.lineTo(radius,chaY1);
            pathCha.lineTo((radius*2)-((halfRadius*3)/2),chaY2);
            pathCha.lineTo(halfRadius*3,chaY1);
            pathCha.lineTo((radius*2)-((halfRadius*3)/2),radius);
            pathCha.lineTo((halfRadius*3)/2,radius);

            //這是下半部分
            pathCha.lineTo((radius*2)-((halfRadius*3)/2),radius);
            pathCha.lineTo(halfRadius*3,radius*2-chaY1);
            pathCha.lineTo((radius*2)-((halfRadius*3)/2),radius*2-chaY2);
            pathCha.lineTo(radius,radius*2-chaY1);
            pathCha.lineTo((halfRadius*3)/2,radius*2-chaY2);
            pathCha.lineTo(halfRadius,radius*2-chaY1);
            canvas.drawPath(pathCha,paintCha);
        }
    }
}

在RecyclerView里應用

recycler.gif

因為這篇文章主要也不是講RecyclerView的側滑實現(xiàn)的,所以這方面的知識大家可以去這里看看掂名。
在RecyclerView應用的代碼也參考自這里据沈。

好了,本篇文章結束了饺蔑。
還有很多不完善的地方锌介,也和UI中國的效果有出入,需要調(diào)整猾警。
才疏學淺孔祸,如有錯誤,歡迎大家批評指正肿嘲。

最后

本篇文章的代碼:CeHuaView

最后的最后

感謝我可愛的女朋友。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末筑公,一起剝皮案震驚了整個濱河市雳窟,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌匣屡,老刑警劉巖封救,帶你破解...
    沈念sama閱讀 222,104評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異捣作,居然都是意外死亡誉结,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評論 3 399
  • 文/潘曉璐 我一進店門券躁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來惩坑,“玉大人掉盅,你說我怎么就攤上這事∫允妫” “怎么了趾痘?”我有些...
    開封第一講書人閱讀 168,697評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長蔓钟。 經(jīng)常有香客問我永票,道長,這世上最難降的妖魔是什么滥沫? 我笑而不...
    開封第一講書人閱讀 59,836評論 1 298
  • 正文 為了忘掉前任侣集,我火速辦了婚禮,結果婚禮上兰绣,老公的妹妹穿的比我還像新娘世分。我一直安慰自己,他們只是感情好狭魂,可當我...
    茶點故事閱讀 68,851評論 6 397
  • 文/花漫 我一把揭開白布罚攀。 她就那樣靜靜地躺著,像睡著了一般雌澄。 火紅的嫁衣襯著肌膚如雪斋泄。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,441評論 1 310
  • 那天镐牺,我揣著相機與錄音炫掐,去河邊找鬼。 笑死睬涧,一個胖子當著我的面吹牛募胃,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播畦浓,決...
    沈念sama閱讀 40,992評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼痹束,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了讶请?” 一聲冷哼從身側響起祷嘶,我...
    開封第一講書人閱讀 39,899評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎夺溢,沒想到半個月后论巍,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,457評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡风响,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,529評論 3 341
  • 正文 我和宋清朗相戀三年嘉汰,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片状勤。...
    茶點故事閱讀 40,664評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡鞋怀,死狀恐怖双泪,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情接箫,我是刑警寧澤攒读,帶...
    沈念sama閱讀 36,346評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站辛友,受9級特大地震影響薄扁,放射性物質發(fā)生泄漏。R本人自食惡果不足惜废累,卻給世界環(huán)境...
    茶點故事閱讀 42,025評論 3 334
  • 文/蒙蒙 一邓梅、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧邑滨,春花似錦日缨、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至哎壳,卻和暖如春毅待,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背归榕。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評論 1 272
  • 我被黑心中介騙來泰國打工尸红, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人刹泄。 一個月前我還...
    沈念sama閱讀 49,081評論 3 377
  • 正文 我出身青樓外里,卻偏偏與公主長得像,于是被迫代替她去往敵國和親特石。 傳聞我的和親對象是個殘疾皇子盅蝗,可洞房花燭夜當晚...
    茶點故事閱讀 45,675評論 2 359

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

  • 來源: http://www.douban.com/group/topic/14820131/ 調(diào)整變量格式: f...
    MC1229閱讀 6,924評論 0 5
  • (轉自http://www.douban.com/group/topic/14820131/,轉自人大論壇) 調(diào)整...
    f382b3d9bdb3閱讀 10,592評論 0 8
  • 談談貝塞爾曲線 最近在做項目的時候姆蘸,需要用到一個動畫墩莫,非常簡單的動畫,簡單到就是直接對一個View做平移… 然而雖...
    雨潤聽潮閱讀 6,007評論 1 16
  • 背景: 給一系列頂點乞旦,如果只是用直線將其中的各個點依次連接起來贼穆,最終形成一個折線圖题山,這種很容易實現(xiàn)兰粉。但是現(xiàn)實...
    狂風無跡閱讀 39,299評論 12 70
  • 什么是貝塞爾曲線,它能夠做什么顶瞳? 貝塞爾曲線的名稱來源于一位就職于雷諾的法國工程師Pierre Bézier玖姑,他在...
    李晨瑋閱讀 2,753評論 2 19