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要求的最終效果
我們先分析下我們主要的實(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)效果
簡(jiǎn)易的demo就這樣了 要添加新的動(dòng)畫(huà)可以自己寫(xiě)一個(gè)新的自定義behavio試一下
demo gitHub 地址:https://github.com/525642022/PercentageBehavior