android 自定義Behavior

CoordinatorLayout是support:design庫(kù)里面的核心空間 可以幫助我們實(shí)現(xiàn)一些炫酷的交互動(dòng)畫(huà)在使用的過(guò)程中我們總是不可避免的遇到一個(gè)類(lèi)Behavior有的時(shí)候我們并沒(méi)有去深入的了解它只是簡(jiǎn)單是設(shè)置一下app:layout_behavior="@string/appbar_scrolling_view_behavior就完事了也不去了解里面具體的實(shí)現(xiàn)原理,其實(shí)它才是我們要實(shí)現(xiàn)一系列炫酷的交互的主要實(shí)現(xiàn)類(lèi)。
那么什么是behavior呢鳖轰?我講去實(shí)現(xiàn)一個(gè)自定義的behavior是大家更加方便的理解他
我們要了解一下再Behavior中可以被重寫(xiě)的類(lèi)

layoutDependsOn():確定使用Behavior的View要依賴(lài)的View的類(lèi)型

onDependentViewChanged():當(dāng)被依賴(lài)的View狀態(tài)改變時(shí)回調(diào)

onDependentViewRemoved():當(dāng)被依賴(lài)的View移除時(shí)回調(diào)

onMeasureChild():測(cè)量使用Behavior的View尺寸

onLayoutChild():確定使用Behavior的View位置

其中如果我們只做動(dòng)畫(huà)主要就是layoutDependsOn() 和 onDependentViewChanged()方法
在這里先來(lái)看看我們UI要求的最終效果


6675987674397022473_Trim.gif

我們先分析下我們主要的實(shí)現(xiàn)過(guò)程:這里面一共包含三個(gè)動(dòng)畫(huà)
(1)頭像移動(dòng)和縮放動(dòng)畫(huà)
(2)文字的透明度動(dòng)畫(huà)
(3)背景的透明動(dòng)畫(huà)
為了方便我們以后的實(shí)現(xiàn) 我先定義的一個(gè)聯(lián)動(dòng)百分比滑動(dòng)的基類(lèi) 可以使子類(lèi)更加方便的去實(shí)現(xiàn)
基類(lèi)代碼


public abstract  class PercentageViewBehavior1<V extends View> extends CoordinatorLayout.Behavior<V> {

    static final int UNSPECIFIED_INT = Integer.MAX_VALUE;
    static final float UNSPECIFIED_FLOAT = Float.MAX_VALUE;


    //需要聯(lián)動(dòng)的View的idq
    private int mDependViewId;
    //聯(lián)動(dòng)滑動(dòng)的終點(diǎn)
    private int mDependTarget;
    //聯(lián)動(dòng)滑動(dòng)的起點(diǎn)
    private int mDependStartY;


    /**
     * Is the values prepared to be use
     */
    private boolean isPrepared;

    PercentageViewBehavior1(Context context, AttributeSet attrs) {
        super(context, attrs);

        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ViewBehavior);
        mDependViewId = a.getResourceId(R.styleable.ViewBehavior_behavior_dependsOn, 0);
        mDependTarget = a.getDimensionPixelOffset(R.styleable.ViewBehavior_behavior_dependTarget, UNSPECIFIED_INT);
        a.recycle();
    }


    /***
     * 初始化一些數(shù)值
     * @param parent
     * @param child
     * @param dependency
     */
    void prepare(CoordinatorLayout parent, V child, View dependency) {
        mDependStartY = (int) dependency.getY();
        isPrepared = true;
    }

    @Override
    public boolean layoutDependsOn(CoordinatorLayout parent, V child, View dependency) {
        // depend on the view that has the same id
        return dependency.getId() == mDependViewId;
    }

    @Override
    public boolean onDependentViewChanged(CoordinatorLayout parent, V child, View dependency) {
        // first time, prepare values before continue
        if (!isPrepared) {
            prepare(parent, child, dependency);
        }
        updateView(child, dependency);
        return false;
    }

    @Override
    public boolean onLayoutChild(CoordinatorLayout parent, V child, int layoutDirection) {
        boolean bool = super.onLayoutChild(parent, child, layoutDirection);
        if (isPrepared) {
            updateView(child, parent.getDependencies(child).get(0));
        }
        return bool;
    }

    /***
     * 根據(jù) dependency 去修改View的狀態(tài)
     * @param child
     * @param dependency
     */
    void updateView(V child, View dependency) {
        float percent = 0;
        float start = 0;
        float current = 0;
        float end = UNSPECIFIED_INT;
        start = mDependStartY;
        current = dependency.getY();
        end = mDependTarget;
        // need to define target value according to the depend type, if not then skip
        if (end != UNSPECIFIED_INT) {
            percent = Math.abs(current - start) / Math.abs(end - start);
        }

        updateViewWithPercent(child, percent > 1 ? 1 : percent);

    }

    /**
     * 根據(jù)百分比去實(shí)現(xiàn)子類(lèi)的狀態(tài)
     *
     * @param child
     * @param percent
     */
     abstract  void updateViewWithPercent(V child, float percent);


}

子類(lèi)只需要去重寫(xiě)updateViewWithPercent(V child, float percent)方法去根據(jù)傳入的百分比去做動(dòng)畫(huà)就好了

后面我們先實(shí)現(xiàn)頭像的移動(dòng)和縮放動(dòng)畫(huà)
首先分解一下動(dòng)畫(huà)
縮放 整個(gè)過(guò)程中
橫向移動(dòng) 前50%
縱向移動(dòng) 后50%

public class SimpleViewBehavior extends PercentageViewBehavior<View> {
   //開(kāi)始的信息
    private int mStartX;
    private int mStartY;
    private int mStartWidth;
    private int mStartHeight;

   //結(jié)束的信息
    private int targetX;
    private int targetY;
    private int targetWidth;
    private int targetHeight;



    public SimpleViewBehavior(Context context, AttributeSet attrs) {
        super(context, attrs);
        // setting values
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ViewBehavior);
        targetX = a.getDimensionPixelOffset(R.styleable.ViewBehavior_behavior_targetX, UNSPECIFIED_INT);
        targetY = a.getDimensionPixelOffset(R.styleable.ViewBehavior_behavior_targetY, UNSPECIFIED_INT);
        targetWidth = a.getDimensionPixelOffset(R.styleable.ViewBehavior_behavior_targetWidth, UNSPECIFIED_INT);
        targetHeight = a.getDimensionPixelOffset(R.styleable.ViewBehavior_behavior_targetHeight, UNSPECIFIED_INT);
        a.recycle();
    }


    @Override
    void prepare(CoordinatorLayout parent, View child, View dependency) {
        super.prepare(parent, child, dependency);

        mStartX = (int) child.getX();
        mStartY = (int) child.getY();
        mStartWidth = child.getWidth();
        mStartHeight = child.getHeight();
        // if parent fitsSystemWindows is true, add status bar height to target y if specified
        if (Build.VERSION.SDK_INT > 16 && parent.getFitsSystemWindows() && targetY != UNSPECIFIED_INT) {
            int result = 0;
            Resources resources = parent.getContext().getResources();
            int resourceId = resources.getIdentifier("status_bar_height", "dimen", "android");
            if (resourceId > 0) {
                result = resources.getDimensionPixelSize(resourceId);
            }
            targetY += result;
        }
    }

    @Override
    void updateViewWithPercent(View child, float percent) {
        float scaleWidth = mStartWidth + ((targetWidth - mStartWidth) * percent);
        float scaleHeight = mStartHeight + ((targetHeight - mStartHeight) * percent);
        if (targetWidth != UNSPECIFIED_INT || targetHeight != UNSPECIFIED_INT) {
            child.setScaleX(scaleWidth / mStartWidth);
            child.setScaleY(scaleHeight / mStartHeight);
        }
        if (percent < 0.5) {
            float newX = targetX == UNSPECIFIED_INT ? 0 : (targetX - mStartX) * percent * 2;
            float newWidth = mStartWidth + ((targetWidth - mStartWidth) * percent * 2);
            newX -= (mStartWidth - newWidth) / 2;
            child.setTranslationX(newX);
        } else {
            float newY = targetY == UNSPECIFIED_INT ? 0 : (float) ((targetY - mStartY) * (percent - 0.5) * 2);
            float newHeight = (float) (mStartHeight + ((targetHeight - mStartHeight) * (percent - 0.5) * 2));
            newY -= (mStartHeight - newHeight) / 2;
            child.setTranslationY(newY);
        }
        child.requestLayout();
    }
}

主要是在復(fù)寫(xiě)的updateViewWithPercent(View child, float percent)放大中進(jìn)行操作
整個(gè)過(guò)程的縮放操作

        float scaleWidth = mStartWidth + ((targetWidth - mStartWidth) * percent);
        float scaleHeight = mStartHeight + ((targetHeight - mStartHeight) * percent);
        if (targetWidth != UNSPECIFIED_INT || targetHeight != UNSPECIFIED_INT) {
            child.setScaleX(scaleWidth / mStartWidth);
            child.setScaleY(scaleHeight / mStartHeight);
        }

前50%的移動(dòng)操作:

            float newX = targetX == UNSPECIFIED_INT ? 0 : (targetX - mStartX) * percent * 2;
            float newWidth = mStartWidth + ((targetWidth - mStartWidth) * percent * 2);
            newX -= (mStartWidth - newWidth) / 2;
            child.setTranslationX(newX);

后50%的移動(dòng)操作:

            float newY = targetY == UNSPECIFIED_INT ? 0 : (float) ((targetY - mStartY) * (percent - 0.5) * 2);
            float newHeight = (float) (mStartHeight + ((targetHeight - mStartHeight) * (percent - 0.5) * 2));
            newY -= (mStartHeight - newHeight) / 2;
            child.setTranslationY(newY);

這就完成了頭像的的整個(gè)移動(dòng)動(dòng)畫(huà)
然后就是文字的隱藏動(dòng)畫(huà) 我們它在前50%就完全隱藏掉
文字隱藏的代碼

public class AlphaViewBehavior extends PercentageViewBehavior<View> {

    private float mStartAlpha;
    private float targetAlpha;

    public AlphaViewBehavior(Context context, AttributeSet attrs) {
        super(context, attrs);
        // setting values
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ViewBehavior);
        targetAlpha = a.getFloat(R.styleable.ViewBehavior_behavior_targetAlpha, UNSPECIFIED_FLOAT);
        a.recycle();
    }


    @Override
    void prepare(CoordinatorLayout parent, View child, View dependency) {
        super.prepare(parent, child, dependency);
        mStartAlpha = child.getAlpha();
    }

    @Override
    void updateViewWithPercent(View child, float percent) {
        if (percent < 0.5) {
            if (targetAlpha != UNSPECIFIED_FLOAT) {
                child.setAlpha(mStartAlpha + (targetAlpha - mStartAlpha) * percent * 2);
            }
        }
        child.requestLayout();
    }


}

背景圖片也是透明度動(dòng)畫(huà)后面可以自己去加
最終實(shí)現(xiàn)效果


test2.gif

簡(jiǎn)易的demo就這樣了 要添加新的動(dòng)畫(huà)可以自己寫(xiě)一個(gè)新的自定義behavio試一下

demo gitHub 地址:https://github.com/525642022/PercentageBehavior

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末讶踪,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子泊交,更是在濱河造成了極大的恐慌乳讥,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,406評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件廓俭,死亡現(xiàn)場(chǎng)離奇詭異云石,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)白指,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)酵紫,“玉大人告嘲,你說(shuō)我怎么就攤上這事〗钡兀” “怎么了橄唬?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,711評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)参歹。 經(jīng)常有香客問(wèn)我仰楚,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,380評(píng)論 1 293
  • 正文 為了忘掉前任僧界,我火速辦了婚禮侨嘀,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘捂襟。我一直安慰自己咬腕,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布涨共。 她就那樣靜靜地躺著,像睡著了一般扒吁。 火紅的嫁衣襯著肌膚如雪火鼻。 梳的紋絲不亂的頭發(fā)上凝危,一...
    開(kāi)封第一講書(shū)人閱讀 51,301評(píng)論 1 301
  • 那天,我揣著相機(jī)與錄音支鸡,去河邊找鬼牧挣。 笑死瀑构,一個(gè)胖子當(dāng)著我的面吹牛寺晌,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播陆赋,決...
    沈念sama閱讀 40,145評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼赖临,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼思杯!你這毒婦竟也來(lái)了色乾?” 一聲冷哼從身側(cè)響起暖璧,我...
    開(kāi)封第一講書(shū)人閱讀 39,008評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤金砍,失蹤者是張志新(化名)和其女友劉穎恕稠,沒(méi)想到半個(gè)月后千扶,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體澎羞,經(jīng)...
    沈念sama閱讀 45,443評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了忌卤。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,795評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖超陆,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情谨娜,我是刑警寧澤,帶...
    沈念sama閱讀 35,501評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站彰阴,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏妻味。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評(píng)論 3 328
  • 文/蒙蒙 一欣福、第九天 我趴在偏房一處隱蔽的房頂上張望责球。 院中可真熱鬧雏逾,春花似錦、人聲如沸栖博。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,731評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春说榆,著一層夾襖步出監(jiān)牢的瞬間虚吟,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,865評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工唱蒸, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留邦鲫,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,899評(píng)論 2 370
  • 正文 我出身青樓神汹,卻偏偏與公主長(zhǎng)得像庆捺,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子屁魏,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評(píng)論 2 354

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