android 自定義Behavior

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

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

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

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

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

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

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


6675987674397022473_Trim.gif

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


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)動的View的idq
    private int mDependViewId;
    //聯(lián)動滑動的終點
    private int mDependTarget;
    //聯(liá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ù)百分比去實現(xiàn)子類的狀態(tài)
     *
     * @param child
     * @param percent
     */
     abstract  void updateViewWithPercent(V child, float percent);


}

子類只需要去重寫updateViewWithPercent(V child, float percent)方法去根據(jù)傳入的百分比去做動畫就好了

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

public class SimpleViewBehavior extends PercentageViewBehavior<View> {
   //開始的信息
    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ù)寫的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);
        }

前50%的移動操作:

            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%的移動操作:

            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);

這就完成了頭像的的整個移動動畫
然后就是文字的隱藏動畫 我們它在前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();
    }


}

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


test2.gif

簡易的demo就這樣了 要添加新的動畫可以自己寫一個新的自定義behavio試一下

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容