原文地址:https://android.jlelse.eu/animate-objects-not-views-250fe7880196

無可否認,我們都喜歡炫酷的動畫效果婉商。沒有用戶會喜歡生硬配乓、沒有過渡和動畫的app黑滴。但同時沒有開發(fā)者愿意花費大量的時間和ValueAnimator
打交道趴泌,特別是當你需要創(chuàng)建很多ValueAnimators
才能達到自己相要的效果。
因此為什么不使用DataBinding
來完成這些復雜的事抒抬?
眾所周知复局,BaseObservable
可以包含標有@Bindable注釋的字段冲簿。
就像這樣:
public class ExpandStateItem extends BaseObservable {
@Bindable
private float padding;
@Bindable
private boolean expanded;
@Bindable
private boolean fast;
}
如果您調用notifyPropertyChanged(BR.field_name)
,任何使用此字段的視圖將被通知到亿昏,然后進行更新民假。所以唯一剩下我們需要做的事情就是把正確的東西綁定到正確的值。
在我的例子中有3個可以綁定的屬性龙优。 讓我們一一看看他們羊异。
第一個值是padding
,我們需要像這樣綁定它
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@{state.padding}"
android:paddingTop="@{state.padding}">
....
</LinearLayout>
state
是在布局文件中聲明的類型為ExpandStateItem
的變量
Android DataBinding
庫甚至知道如何設置padding
屬性彤断,因此您不需要自己創(chuàng)建@BindingAdapter
野舶。
現(xiàn)在如果您為ExpandStateItem
對象的padding
設置某個值,您會看到它對應的所有視圖實際上會改變它的padding
值宰衙。
如果您創(chuàng)建一些動畫并在動畫的CallBack中更改此值平道,則所有視圖將相應地進行動畫。
這里是一個例子供炼。 state
是一個在RecyclerView
中綁定到RecyclerView.ViewHolders
的數(shù)組一屋。
private ValueAnimator paddingAnimator = ValueAnimator.ofFloat(MAX_MARGIN, MIN_MARGIN);
paddingAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
for (ExpandStateItem item :
states) {
item.setPading((float) animation.getAnimatedValue());
}
}
});
下一個變量是expanded
窘疮。如果您注意到文章頂部的GIF圖像,它有一些可伸縮的卡片冀墨,它們隨著expanded
屬性的改變而擴展/折疊闸衫。 它是這樣綁定?的:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:visibility="gone"
app:expand="@{state.expanded}"
app:fastExpand="@{state.fast}">
...
</LinearLayout>
另外你可能注意到了app:fastExpand
,稍后我會告訴你诽嘉。
對于app:expand
屬性蔚出,我們需要編寫我們自己的@BindingAdapter。
@BindingAdapter({"expand", "fastExpand"})
public static void expandView(View view, boolean expand, boolean fast) {
if (expand) {
ViewAnimationUtils.expand(view, null, fast);
} else if (view.getHeight() != 0) {
ViewAnimationUtils.collapse(view, null, fast);
}
}
現(xiàn)在虫腋,如果我向你展示ViewAnimationUtils
類骄酗,我敢打賭你會弄清楚為什么我們需要fastExpand
屬性。
package com.wldev.expandablecardviewlist.extra;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.Transformation;
public class ViewAnimationUtils {
public static void expand(final View v, final AnimationEndCallback callback, boolean fast) {
v.measure(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
final int targtetHeight = v.getMeasuredHeight();
v.getLayoutParams().height = 0;
v.setVisibility(View.VISIBLE);
Animation a = new Animation() {
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
v.getLayoutParams().height = interpolatedTime == 1
? ViewGroup.LayoutParams.WRAP_CONTENT
: (int) (targtetHeight * interpolatedTime);
v.requestLayout();
}
@Override
public boolean willChangeBounds() {
return true;
}
};
int duration = 0;
if (!fast) {
duration = (int) (targtetHeight / v.getContext().getResources().getDisplayMetrics().density);
}
a.setDuration(duration);
a.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
if (callback != null)
callback.onAnimationEnded();
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
v.startAnimation(a);
}
public static void collapse(final View v, final AnimationEndCallback callback, boolean fast) {
final int initialHeight = v.getMeasuredHeight();
Animation a = new Animation() {
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
if (interpolatedTime == 1) {
v.setVisibility(View.GONE);
} else {
v.getLayoutParams().height = initialHeight - (int) (initialHeight * interpolatedTime);
v.requestLayout();
}
}
@Override
public boolean willChangeBounds() {
return true;
}
};
int duration = 0;
if (!fast) {
duration = (int) (initialHeight / v.getContext().getResources().getDisplayMetrics().density);
}
a.setDuration(duration);
a.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
if (callback != null)
callback.onAnimationEnded();
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
v.startAnimation(a);
}
interface AnimationEndCallback {
void onAnimationEnded();
}
}
通過將fastExpand
指定為true悦冀,將不會有動畫時長趋翻。
但是為什么我們需要移除動畫時長?事實上盒蟆,如果你想在可回收的列表上使用它嘿歌,完全不必這樣做。當view將被回收并再次使用(RecyclerView
里是onBindViewHolder
)茁影,所有動畫將再次出現(xiàn)在此特定視圖中,因此需要指定我們只想展開的視圖丧凤,并且不展示進度募闲。
例子就這樣,如何與視圖進行交互(動畫)愿待,而不會觸及視圖(實際上你會浩螺,但是間接地)。 你可以用它做的事情只會受到你想象力的限制仍侥。View
的margins
, paddings
, width
, height
要出,TextView
的lines count
, color
, taste (no)
, thousands of them.。
我的示例的源代碼在GitHub上here农渊。
Thanks for attention and happy coding :)