自定義View(2) -- 58同城加載動畫

先上圖

思考步驟還是和上一篇講的一樣芦圾,相同的套路:
自定義View(1)--QQ運動計步器

構(gòu)造器不說了憔恳,正常情況一般三個都會寫,這篇我沒從attr里面寫東西婴洼,所以這一步跳過了,我們直接來要真正思考的地方:首先我們這里實現(xiàn)的話有很多種方式撼嗓,可以用圖片柬采,可以自己繪制欢唾,這里我們選擇自己繪制,但是繪制的話粉捻,我們又要考慮這個動畫是需要我們自己算坐標然后重繪礁遣?還是寫成單獨的一個view然后通過動畫實現(xiàn),這里我選擇了后者肩刃,原因有三個:第一個是如果在 view內(nèi)部寫相關(guān)的移動旋轉(zhuǎn)邏輯的話祟霍,計算量不用說增加了,這個是很費時間的盈包,而且很容易出錯沸呐;第二個是因為你不停的調(diào)用invalidate,看了源碼的都知道呢燥,調(diào)用這個函數(shù)的會做很多工作崭添,造成不必要的gpu耗費,這樣不好叛氨;第三個是實用性呼渣,如果以后我們需要這樣一個圖像,我們可以直接把這個形狀的view復制過去就可以使用力试,所以我們理清思路徙邻。
首先,上面跳動的view作為一個單獨的view畸裳,下面的弧形陰影也做為一個單獨的view,然后在外層通過一個viewGroup把他們組裝起來并且控制相關(guān)的代碼和其性能的優(yōu)化。
我們先繪制圖形的 view淳地,也就是上下跳動的哪個view怖糊。我們 需要組裝,所以我們肯定要重寫onMeasure設置其寬高颇象,這里我打算指定他的高度和寬度為圓的半徑的兩倍:

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

    /**
     * Determines the width of this view
     *
     * @param measureSpec A measureSpec packed into an int
     * @return The width of the view, honoring constraints from measureSpec
     */
    private int measureWidth(int measureSpec) {
        int result = 0;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);

        if (specMode == MeasureSpec.EXACTLY) {
            result = specSize;
        } else {
            result = mRadio * 2;
        }

        return result;
    }

    /**
     * Determines the height of this view
     *
     * @param measureSpec A measureSpec packed into an int
     * @return The height of the view, honoring constraints from measureSpec
     */
    private int measureHeight(int measureSpec) {
        int result = 0;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);
        if (specMode == MeasureSpec.EXACTLY) {
            result = specSize;
        } else {
            result = 2 * mRadio;
        }
        return result;
    }

接下來我們繪制圖形伍伤,考略到我們需要變換不同形狀的view,所以我寫的時候分別寫了三個繪制不同圖案的函數(shù),然后添加一個flag判斷繪誰即可

@Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        initCenterPoint();

        if (mFlog == 0)
            drawCircle(canvas);
        else if (mFlog == 1)
            drawRect(canvas);
        else
            drawTriangle(canvas);
    }

    private void initCenterPoint() {
        if (mCenterX == -1 || mCenterY == -1) {
            mCenterX = getMeasuredWidth() / 2;
            mCenterY = getMeasuredHeight() / 2;
         //   setPivotX(mCenterX);
         //  setPivotY(mCenterY);
        }
    }

    /**
     * 畫圓
     *
     * @param canvas
     */
    private void drawCircle(Canvas canvas) {
        canvas.drawCircle(mCenterX, mCenterY, mRadio, mCirclePaint);
    }

   


    /**
     * 畫正方形
     *
     * @param canvas
     */
    private void drawRect(Canvas canvas) {
        Rect rect = new Rect(mCenterX - mRadio,
                mCenterY - mRadio,
                mCenterX + mRadio,
                mCenterY + mRadio);
        canvas.drawRect(rect, mRectPaint);
    }


    public void setFlog(int mFlog) {
        this.mFlog = mFlog;
        invalidate();
    }

    /**
     * 繪制三角形
     *
     * @param canvas
     */
    private void drawTriangle(Canvas canvas) {
        Path path = new Path();
        path.moveTo(mCenterX, mCenterY - mRadio);
        path.lineTo(mCenterX - mRadio, mCenterY + mRadio);
        path.lineTo(mCenterX + mRadio, mCenterY + mRadio);
        canvas.drawPath(path, mTrianglePaint);
    }

測試一下這個圖案已經(jīng)繪制完成了遣钳,接下來我們繪制下面的陰影扰魂,同樣的我們需要先測量,我這里設置寬高為半徑值的2/3,寬為兩倍的半徑:

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

    /**
     * Determines the width of this view
     *
     * @param measureSpec A measureSpec packed into an int
     * @return The width of the view, honoring constraints from measureSpec
     */
    private int measureWidth(int measureSpec) {
        int result = 0;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);

        if (specMode == MeasureSpec.EXACTLY) {
            result = specSize;
        } else {
            result = mRadio * 2;
        }

        return result;
    }

    /**
     * Determines the height of this view
     *
     * @param measureSpec A measureSpec packed into an int
     * @return The height of the view, honoring constraints from measureSpec
     */
    private int measureHeight(int measureSpec) {
        int result = 0;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);
        if (specMode == MeasureSpec.EXACTLY) {
            result = specSize;
        } else {
            result = 2 * mRadio / 3;
        }
        return result;
    }

接下來我們繪制圖案蕴茴,我在繪制這個圖案的時候是采用drawArc的方式劝评,所以我們需要計算一下它的范圍,我這采用的方案如下:
因為我們要上面的那塊橫線挨著物塊view掉下的位置,所以rect的中心點就是這個,然后計算如下:

           int left = getWidth() / 2 - mRadio;
            int top = -getHeight() / 2;
            int right = getWidth() / 2 + mRadio;
            int bottom = getHeight() / 2;
            mRectF = new RectF(left, top, right, bottom);

其實getHeight就是2/3的半徑
我們繪制的時候倦淀,繪制180°即可蒋畜,:

   @Override
    protected void onDraw(Canvas canvas) {
        if (mRectF == null) {
            int left = getWidth() / 2 - mRadio;
            int top = -getHeight() / 2;
            int right = getWidth() / 2 + mRadio;
            int bottom = getHeight() / 2;
            mRectF = new RectF(left, top, right, bottom);
        }
        canvas.drawArc(mRectF, 0, 180, false, mArcPaint);
    }

最后我們將這兩個圖案組裝起來,加上動畫即可:

     public void startAnim() {
        if (mAnimatorSet == null) {
            mAnimatorSet = new AnimatorSet();
            //up
            ObjectAnimator rotationAnim = ObjectAnimator.ofFloat(mShapeView, "rotation", 0.0f, 360.0f);
            rotationAnim.setDuration(JUMP_UP_TIME);
            rotationAnim.setInterpolator(new AccelerateDecelerateInterpolator());//先快后慢

            ObjectAnimator translationAnimUp = ObjectAnimator.ofFloat(mShapeView, "translationY", 0, -JUMP_MAX_HEIGHT);
            translationAnimUp.setDuration(JUMP_UP_TIME);
            translationAnimUp.setInterpolator(new AccelerateDecelerateInterpolator());//先快后慢

            ObjectAnimator scaleXAnimUp = ObjectAnimator.ofFloat(mArcView, "scaleX", 1.0f, 0.2f);
            scaleXAnimUp.setDuration(JUMP_UP_TIME);
            scaleXAnimUp.setInterpolator(new AccelerateDecelerateInterpolator());//先快后慢

            ObjectAnimator scaleYAnimUp = ObjectAnimator.ofFloat(mArcView, "scaleY", 1.0f, 0.2f);
            scaleYAnimUp.setDuration(JUMP_UP_TIME);
            scaleYAnimUp.setInterpolator(new AccelerateDecelerateInterpolator());//先快后慢

            //down
            ObjectAnimator translationAnimDown = ObjectAnimator.ofFloat(mShapeView, "translationY", -JUMP_MAX_HEIGHT, 0);
            translationAnimDown.setDuration(JUMP_UP_TIME);
            translationAnimDown.setInterpolator(new AccelerateInterpolator());//先慢后快

            ObjectAnimator scaleXAnimXDown = ObjectAnimator.ofFloat(mArcView, "scaleX", 0.2f, 1.0f);
            scaleXAnimXDown.setDuration(JUMP_UP_TIME);
            scaleXAnimXDown.setInterpolator(new AccelerateInterpolator());//先慢后快

            ObjectAnimator scaleYAnimDown = ObjectAnimator.ofFloat(mArcView, "scaleY", 0.2f, 1.0f);
            scaleYAnimDown.setDuration(JUMP_UP_TIME);
            scaleYAnimDown.setInterpolator(new AccelerateInterpolator());//先慢后快

            mAnimatorSet.play(translationAnimUp)
                    .with(rotationAnim)
                    .with(scaleXAnimUp)
                    .with(scaleYAnimUp)
                    .before(translationAnimDown)
                    .before(scaleXAnimXDown)
                    .before(scaleYAnimDown);
            mAnimatorSet.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    super.onAnimationEnd(animation);
                    mFlog++;//圓形 0-->1方形 -->2 三角形 -->3->0---->
                    if (mFlog > 2) {
                        mFlog = 0;
                    }
                    mShapeView.setFlog(mFlog);
                    startAnim();
                }
            });
        }
        mAnimatorSet.start();
    }

這樣的話效果實現(xiàn)了撞叽,感覺沒什么問題了姻成。但是不要忘記了優(yōu)化
優(yōu)化
在這里我們主要優(yōu)化這個動畫對應Activity的生命周期 ,讓其可見的時候加載動畫插龄,不可見的時候停止動畫。主要使用Application.ActivityLifecycleCallbacks這個接口實現(xiàn),具體實現(xiàn)的代碼如下:

 @Override
    protected void onAttachedToWindow() {
        mActivity.getApplication().registerActivityLifecycleCallbacks(animLifecyleCallback);
        super.onAttachedToWindow();
    }

    @Override
    protected void onDetachedFromWindow() {
        mActivity.getApplication().unregisterActivityLifecycleCallbacks(animLifecyleCallback);
        super.onDetachedFromWindow();
    }
    
    private SimpleActivityLifecycleCallbacks animLifecyleCallback = new SimpleActivityLifecycleCallbacks() {
        @Override
        public void onActivityResumed(Activity activity) { // 頁面第一次啟動的時候不會執(zhí)行
            if (activity == mActivity)
                startAnim();
            super.onActivityResumed(activity);
        }

        @Override
        public void onActivityPaused(Activity activity) {
            if (activity == mActivity)
                stopAnim();
            super.onActivityPaused(activity);
        }
    };

這樣就更加優(yōu)雅了科展。
源碼github下載地址:
https://github.com/ChinaZeng/CustomView

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末均牢,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子才睹,更是在濱河造成了極大的恐慌膨处,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件砂竖,死亡現(xiàn)場離奇詭異真椿,居然都是意外死亡,警方通過查閱死者的電腦和手機乎澄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門突硝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人置济,你說我怎么就攤上這事解恰。” “怎么了浙于?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵护盈,是天一觀的道長。 經(jīng)常有香客問我羞酗,道長腐宋,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任檀轨,我火速辦了婚禮胸竞,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘参萄。我一直安慰自己卫枝,他們只是感情好,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布讹挎。 她就那樣靜靜地躺著校赤,像睡著了一般。 火紅的嫁衣襯著肌膚如雪筒溃。 梳的紋絲不亂的頭發(fā)上马篮,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天,我揣著相機與錄音铡羡,去河邊找鬼积蔚。 笑死,一個胖子當著我的面吹牛烦周,可吹牛的內(nèi)容都是我干的尽爆。 我是一名探鬼主播怎顾,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼漱贱!你這毒婦竟也來了槐雾?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤幅狮,失蹤者是張志新(化名)和其女友劉穎募强,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體崇摄,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡擎值,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了逐抑。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鸠儿。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖厕氨,靈堂內(nèi)的尸體忽然破棺而出进每,到底是詐尸還是另有隱情,我是刑警寧澤命斧,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布田晚,位于F島的核電站,受9級特大地震影響国葬,放射性物質(zhì)發(fā)生泄漏贤徒。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一胃惜、第九天 我趴在偏房一處隱蔽的房頂上張望泞莉。 院中可真熱鬧,春花似錦船殉、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至堡僻,卻和暖如春糠惫,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背钉疫。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工硼讽, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人牲阁。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓固阁,卻偏偏與公主長得像壤躲,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子备燃,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345

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

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,501評論 25 707
  • 在iOS中隨處都可以看到絢麗的動畫效果碉克,實現(xiàn)這些動畫的過程并不復雜,今天將帶大家一窺ios動畫全貌并齐。在這里你可以看...
    每天刷兩次牙閱讀 8,465評論 6 30
  • 【作者】小馬哥 【課程導師】劉艷 【導圖內(nèi)容】: 對照這6點漏麦,看看你的手機是不是真正的全網(wǎng)通 各大廠商春季產(chǎn)品發(fā)布...
    Edward198548閱讀 101評論 0 0
  • 地鐵門緩緩打開,無數(shù)雙焦灼的眼睛盯著漸寬的縫隙况褪,一個優(yōu)美的側(cè)身撕贞,腦袋間游向出口,一上岸测垛,真不明白早晨的太陽也這么耀...
    一肩月光閱讀 244評論 0 0
  • 生活里、工作中所有的痛苦都源自于與一些“志不同疙描,道不合”的人即便水火難容也依然要走“道不同”也要“相為謀”的路诚隙。 ...
    高手如林閱讀 444評論 1 2