1 Android屬性動(dòng)畫中ofXX函數(shù)概述
前面一篇屬性動(dòng)畫文章中講解了如何利用ObjectAnimator實(shí)現(xiàn)幾種補(bǔ)間動(dòng)畫效果和如何自定義ObjectAnimator屬性,其中用到了ObjectAnimator對象的ofInt,ofFLoat,ofObject,但這些函數(shù)都有眾多的重載函數(shù)屈溉,這一篇接著講解ObjectAnimator眾多的ofXXX函數(shù)的用法塞关。
觀看本篇文章請務(wù)必先看Android動(dòng)畫之ValueAnimator用法和自定義估值器
ObjectAnimator of函數(shù)列表:
ofArgb(Object target, String propertyName, int... values) api21可用
ofArgb(T target, Property<T, Integer> property, int... values) api21可用
ofFloat(Object target, String propertyName, float... values) api11 可用
ofFloat(Object target, String xPropertyName, String yPropertyName, Path path) api21可用
ofFloat(T target, Property<T, Float> property, float... values) api14可用
ofFloat(T target, Property<T, Float> xProperty, Property<T, Float> yProperty, Path path) api21可用
ofInt(Object target, String propertyName, int... values) api11可用
ofInt(Object target, String xPropertyName, String yPropertyName,Path path) api21可用
ofInt(T target, Property<T, Integer> property, int... values) api14可用
ofInt(T target, Property<T, Integer> xProperty,Property<T, Integer> yProperty, Path path) api21可用
ofMultiFloat(Object target, String propertyName,float[][] values) api21可用
ofMultiFloat(Object target, String propertyName, Path path) api21可用
ofMultiFloat(Object target, String propertyName,
TypeConverter<T, float[]> converter, TypeEvaluator<T> evaluator, T... values) api21可用
ofMultiInt(Object target, String propertyName, int[][] values) api21可用
ofMultiInt(Object target, String propertyName, Path path) api21可用
ofMultiInt(Object target, String propertyName,
TypeConverter<T, int[]> converter, TypeEvaluator<T> evaluator, T... values) api21可用
ofObject(Object target, String propertyName,
@Nullable TypeConverter<PointF, ?> converter, Path path) api21可用
ofObject(Object target, String propertyName, TypeEvaluator evaluator, Object... values) api11
ofObject(T target, Property<T, P> property,
TypeConverter<V, P> converter, TypeEvaluator<V> evaluator, V... values) api21
ofObject(T target, @NonNull Property<T, V> property,
@Nullable TypeConverter<PointF, V> converter, Path path) api2可用
ofObject(T target, Property<T, V> property, TypeEvaluator<V> evaluator, V... values) api14可用
ofPropertyValuesHolder(Object target, PropertyValuesHolder... values) api11可用
2 OfFloat & ofInt
把ofFLoat和ofInt兩個(gè)函數(shù)一起講解是因?yàn)檫@兩個(gè)函數(shù)除了類型不一樣,重載函數(shù)很相似子巾。
ofFloat(Object target, String propertyName, float... values)
ofFloat(Object target, String xPropertyName, String yPropertyName, Path path)
ofFloat(T target, Property<T, Float> property, float... values)
ofFloat(T target, Property<T, Float> xProperty, Property<T, Float> yProperty, Path path)
ofInt(Object target, String propertyName, int... values)
ofInt(Object target, String xPropertyName, String yPropertyName,Path path)
ofInt(T target, Property<T, Integer> property, int... values)
ofInt(T target, Property<T, Integer> xProperty,Property<T, Integer> yProperty, Path path)
ObjectAnimator 的ofFloat和ofInt函數(shù)用法相似帆赢,所以只講解ofFloat,
ofFloat(Object target, String propertyName, float... values)
這個(gè)函數(shù)比較簡單线梗,第一個(gè)參數(shù)傳入目標(biāo)對象椰于,第二個(gè)參數(shù)傳入要改變的屬性(配合setXX函數(shù),關(guān)于如何定義propertyName前面的文章中已經(jīng)說明)仪搔,第三個(gè)參數(shù)是個(gè)漸變屬性瘾婿,可以傳多個(gè)值。
代碼示例:
實(shí)現(xiàn)TextView的旋轉(zhuǎn):
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(mTextView, "rotation", 0, 270);
objectAnimator.setDuration(3000);
objectAnimator.setRepeatCount(-1);
objectAnimator.setInterpolator(new LinearInterpolator());
objectAnimator.start();
ofFloat(Object target, String xPropertyName, String yPropertyName, Path path)
可以同時(shí)操縱兩個(gè)參數(shù)變化,實(shí)現(xiàn)動(dòng)畫偏陪。
參數(shù)說明:
target:動(dòng)畫目標(biāo)對象,這個(gè)目標(biāo)對象有些特別抢呆,沿著一條路徑Path能夠使用兩個(gè)屬性,路徑Path動(dòng)畫在二維空間中移動(dòng)笛谦,由動(dòng)畫坐標(biāo)(x,y)決定效果抱虐,(重要)所以對象必須有兩個(gè)函數(shù)一個(gè)是setNameX(),另外一個(gè)是setNameY(),類似view的setTranslationX,SetTranslationY饥脑,當(dāng)然也可以自己定義屬性恳邀,同時(shí)對應(yīng)的xPropertyName和yPropertyName分別為translationX和translationY。
xPropertyName:Path對應(yīng)的X軸方向的屬性值灶轰,
yPropertyName:Path對應(yīng)的Y軸方向的屬性值轩娶,
path:動(dòng)畫路徑。
代碼示例:
TextView 在X軸和Y軸方向上移動(dòng)
Path path = new Path();
path.moveTo(0,0);
path.lineTo(50,50);
path.lineTo(100,20);
path.lineTo(900,400);
path.lineTo(500,1000);
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(mTextView, "translationX","translationY",path );
objectAnimator.setDuration(3000);
objectAnimator.setRepeatCount(-1);
objectAnimator.setInterpolator(new LinearInterpolator());
objectAnimator.start();
上面的示例代碼利用了translationX和translationX屬性框往,這兩個(gè)屬性是View的自帶的屬性鳄抒,同時(shí)也可以是兩個(gè)互不相干的屬性,可以實(shí)現(xiàn)類似組合動(dòng)畫的效果椰弊。
Path path = new Path();
path.moveTo(0,0);
path.lineTo(50,1);
path.lineTo(100,2);
path.lineTo(900,0.5f);
path.lineTo(500,1);
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(mTextView, "translationX","scaleX",path );
objectAnimator.setDuration(3000);
objectAnimator.setRepeatCount(-1);
objectAnimator.setInterpolator(new LinearInterpolator());
objectAnimator.start();
ofFloat(T target, Property<T, Float> property, float... values)
參數(shù)說明:
target:動(dòng)畫目標(biāo)對象
property:動(dòng)畫作用的屬性许溅,有了這個(gè)屬性對象, 就可以不用寫屬性對應(yīng)的字段值秉版,類似不用寫“scale”
values:動(dòng)畫取值贤重,如果是一個(gè)值則將target開始的狀態(tài)作為開始值,將values的一個(gè)值清焕,作為結(jié)束值并蝗,如果是兩個(gè)值則第一個(gè)為動(dòng)畫開始值,第二個(gè)為動(dòng)畫結(jié)束值秸妥。
這個(gè)函數(shù)用到了Property屬性滚停,是API14添加的方法,不知道大家注意到?jīng)]有粥惧,每次使用屬性動(dòng)畫键畴,我們都需要記得目標(biāo)對象的setXXX函數(shù)后面的相應(yīng)字符串,雖然不復(fù)雜但有時(shí)確實(shí)會(huì)記不清突雪,需要再次確認(rèn)起惕,而這個(gè)帶有Property的函數(shù)就大大簡化了這個(gè)過程。
Android為我們提供了簡單的常量對象來實(shí)現(xiàn)旋轉(zhuǎn)動(dòng)畫:
ObjectAnimator.ofFloat(mTextView, View.ROTATION, 0,30);
這里的View.ROTATION就是個(gè)Property對象咏删,可以簡單地實(shí)現(xiàn)旋轉(zhuǎn)的屬性動(dòng)畫惹想。
View.ROTATION源碼
public static final Property<View, Float> ROTATION = new FloatProperty<View>("rotation") {
@Override
public void setValue(View object, float value) {
object.setRotation(value);
}
@Override
public Float get(View object) {
return object.getRotation();
}
};
View.ROTATION示例
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(mTextView, View.ROTATION, 0,270);
objectAnimator.setDuration(3000);
objectAnimator.setRepeatCount(-1);
objectAnimator.setInterpolator(new LinearInterpolator());
objectAnimator.start();
View中具有和View.ROTATION作用的常量還有如下:
ALPHA,TRANSLATION_X督函,TRANSLATION_Y,TRANSLATION_Z,X,Y,ROTATION, ROTATION_X , ROTATION_Y,SCALE_X,SCALE_Y嘀粱。
自定義Property
分析上面常量的他們都是實(shí)現(xiàn)了FloatProperty激挪,或者IntProperty然后重寫setValue和get方法,而FloatProperty,IntProperty又是繼承了Property草穆,所以我們可以通過實(shí)現(xiàn)FloatPropety,IntProperty或者直接實(shí)現(xiàn)Property來自定義Property搓译。
自定義Property:
public static class MyProperty extends Property<TextView,String>{
public MyProperty(String name) {
super(String.class, name);
}
@Override
public String get(TextView object) {
return object.getText().toString();
}
@Override
public void set(TextView object, String value) {
object.setText(value);
}
}
自定義估值器:
public static class IntEvaluator implements TypeEvaluator<String>{
@Override
public String evaluate(float fraction, String startValue, String endValue) {
int startInt = Integer.parseInt(startValue) ;
int endInt = Integer.parseInt(endValue);
int cur = (int) (startInt + fraction * (endInt - startInt));
return cur+"";
}
}
使用自定義Property:
IntEvaluator intEvaluator = new IntEvaluator();
MyProperty property = new MyProperty("text");
ObjectAnimator objectAnimator = ObjectAnimator.ofObject(mTextView,property,intEvaluator,"1", "10");
objectAnimator.setDuration(3000);
objectAnimator.setRepeatCount(-1);
objectAnimator.setInterpolator(new LinearInterpolator());
objectAnimator.start();
ofFloat(T target, Property<T, Float> xProperty, Property<T, Float> yProperty, Path path)
函數(shù)也用到了Property屬性悲柱,需要結(jié)合property和Path,類似上面函數(shù)的用法些己,不再說明豌鸡。
3 ofArgb 顏色屬性動(dòng)畫
ofArgb(Object target, String propertyName, int... values) 對顏色屬性進(jìn)行動(dòng)畫。
參數(shù)說明:
target:動(dòng)畫作用對象
propertyName:動(dòng)畫作用的屬性
values:動(dòng)畫使用的可變參數(shù)
代碼示例:
ObjectAnimator objectAnimator = ObjectAnimator.ofArgb(mTextView,"backgroundColor", Color.RED, Color.GREEN);
objectAnimator.setDuration(3000);
objectAnimator.setRepeatCount(-1);
objectAnimator.setInterpolator(new LinearInterpolator());
objectAnimator.start();
ofArgb(T target, Property<T, Integer> property, int... values)
用到了Property屬性段标,但是View中沒有類似ROTATION屬性的對顏色屬性的簡寫涯冠,可以自定義Property,下面的例子只是數(shù)值的漸變逼庞,如果真的需要顏色漸變蛇更,需要設(shè)置顏色估值器:
public static class MyProperty extends Property<TextView,Integer>{
public MyProperty(String name) {
super(Integer.class, name);
}
@Override
public Integer get(TextView object) {
Drawable drawable = object.getBackground();
if (drawable instanceof ColorDrawable){
return ((ColorDrawable) drawable).getColor();
}
return Color.YELLOW;
}
@Override
public void set(TextView object, Integer value) {
object.setBackgroundColor(value);
}
}
使用:
MyProperty property = new MyProperty("background");
ObjectAnimator objectAnimator = ObjectAnimator.ofArgb(mTextView,property, Color.RED, Color.GREEN);
objectAnimator.setDuration(3000);
objectAnimator.setRepeatCount(-1);
objectAnimator.setInterpolator(new LinearInterpolator());
objectAnimator.start();
4 ofMultiFloat,ofMultiInt
被稱為多參數(shù)布局,用的不太多赛糟,下面簡單介紹:
ofMultiFloat(Object target, String propertyName,float[][] values)
ofMultiFloat(Object target, String propertyName, Path path)
ofMultiFloat(Object target, String propertyName,
TypeConverter<T, float[]> converter, TypeEvaluator<T> evaluator, T... values)
ofMultiInt(Object target, String propertyName, int[][] values)
ofMultiInt(Object target, String propertyName, Path path)
ofMultiInt(Object target, String propertyName,
TypeConverter<T, int[]> converter, TypeEvaluator<T> evaluator, T... values)
ofMultiFloat和ofMultiInt用法相似派任,由于上面只介紹了ofFloat,下面只介紹ofMultiInt方法:
ofMultiInt(Object target, String propertyName, int[][] values)
參數(shù)說明:
propertyName:進(jìn)行動(dòng)畫的屬性名
values[][]:二維數(shù)組璧南,至少兩組數(shù)據(jù)掌逛,每個(gè)values[]中存放一個(gè)setter函數(shù)中所有的參數(shù),然后從values[0]中取值為動(dòng)畫開始值司倚,從values[最后一組]中取值為動(dòng)畫最后的值豆混,如果之間還有值,就作為過渡动知,從values[0]-values[1]-........(大家明白的)
需要自定義view添加getter皿伺,setter函數(shù)。
代碼示例:
public class ViewDemo24 extends android.support.v7.widget.AppCompatTextView {
public ViewDemo24(Context context) {
this(context,null,0);
}
public ViewDemo24(Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0);
}
public ViewDemo24(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
}
public void setMulText(int data1,int data2){
String data = "";
data = data + data1 + data2;
setText(data);
}
public String getMulText(){
return getText().toString();
}
}
使用:
int[][] data = {{1,9},{4,12}} ;
ObjectAnimator objectAnimator = ObjectAnimator.ofMultiInt(viewDemo24,"mulText",data);
objectAnimator.setDuration(3000);
objectAnimator.setRepeatCount(-1);
objectAnimator.setInterpolator(new LinearInterpolator());
objectAnimator.start();
ofMultiInt(Object target, String propertyName, Path path)
用法和上面的函數(shù)類似盒粮,只不過把二維數(shù)組換成了Path心傀,并且setter函數(shù)只能接收兩個(gè)int參數(shù),從path中取動(dòng)畫開始值和結(jié)束值(從path.moveTo中取動(dòng)畫開始值拆讯,后面的值為動(dòng)畫結(jié)束值)脂男。
代碼示例
**自定義view還用上面的ViewDemo24 **
Path path = new Path();
path.moveTo(0,6);
path.lineTo(5,9);
ObjectAnimator objectAnimator = ObjectAnimator.ofMultiInt(viewDemo24,"mulText",path);
objectAnimator.setDuration(3000);
objectAnimator.setRepeatCount(-1);
objectAnimator.setInterpolator(new LinearInterpolator());
objectAnimator.start();
ofMultiInt(Object target, String propertyName,TypeConverter<T, int[]> converter, TypeEvaluator<T> evaluator, T... values)
Converter:把int[]數(shù)組轉(zhuǎn)換成需要的T類型,然后利用估值器計(jì)算T 得到ObjectAnimator需要的類型种呐。
依然用上面的自定義view 宰翅,setter函數(shù)需要兩個(gè)參數(shù):
用到的自定義T類型:
public static class Point {
int x;
int y;
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
}
自定義類型轉(zhuǎn)換,把上面自定義的Point類型轉(zhuǎn)換成int[]數(shù)組:
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public static class IntConverter extends TypeConverter <Point ,int[]>{
public IntConverter(Class<Point> fromClass, Class<int[]> toClass) {
super(fromClass, toClass);
}
@Override
public int[] convert(Point value) {
int[] intarr = {value.getX(),value.getY()};
return intarr;
}
}
自定義估值器:
public static class PointEvaluator implements TypeEvaluator<Point> {
@Override
public Point evaluate(float fraction, Point startValue, Point endValue) {
int startxInt = startValue.getX() ;
int endxInt = endValue.getX();
int curx = (int) (startxInt + fraction * (endxInt - startxInt));
int startyInt = startValue.getY() ;
int endyInt = endValue.getY();
int cury = (int) (startyInt + fraction * (endyInt - startyInt));
Point point = new Point();
point.setX(curx);
point.setY(cury);
return point;
}
}
代碼中使用:
IntConverter intConverter = new IntConverter(Point.class,int[].class);
PointEvaluator pointEvaluator = new PointEvaluator();
Point point1 = new Point();
point1.setX(1);
point1.setY(5);
Point point2 = new Point();
point2.setX(4);
point2.setY(9);
ObjectAnimator objectAnimator = ObjectAnimator.ofMultiInt(viewDemo24,"mulText",intConverter,pointEvaluator,point1,point2);
objectAnimator.setDuration(3000);
objectAnimator.setRepeatCount(-1);
objectAnimator.setInterpolator(new LinearInterpolator());
objectAnimator.start();
5 ofObject
對對象進(jìn)行動(dòng)畫:
ofObject(Object target, String propertyName,
@Nullable TypeConverter<PointF, ?> converter, Path path)
ofObject(Object target, String propertyName, TypeEvaluator evaluator, Object... values)
ofObject(T target, Property<T, P> property,
TypeConverter<V, P> converter, TypeEvaluator<V> evaluator, V... values)
ofObject(T target, @NonNull Property<T, V> property,
@Nullable TypeConverter<PointF, V> converter, Path path)
ofObject(T target, Property<T, V> property, TypeEvaluator<V> evaluator, V... values)
上面講解了ofInt ,ofFloat,ofMultiInt,ofMultiFloat等函數(shù)爽室,仔細(xì)觀察上面的ofObject函數(shù)汁讼,可以分析得到ofObject把類型泛型化了,每個(gè)函數(shù)都提供了TypeEvaluator供ObjectAnimator識別參數(shù),所以ofObject的用法和ofInt嘿架,ofFloat相同瓶珊。
ofObject(T target, Property<T, V> property, TypeEvaluator<V> evaluator, V... values)
參數(shù)說明:
target:動(dòng)畫目標(biāo)對象
property:自定義property,內(nèi)部調(diào)用getter耸彪,setter函數(shù)伞芹,不用再指定propertyName
evaluator:估值器,生成動(dòng)畫所需對象
values:動(dòng)畫傳入?yún)?shù)
利用ofObject 實(shí)現(xiàn)view的移動(dòng)
自定義類:
public static class Point {
int x;
int y;
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
}
自定義property
public static class MyProperty2 extends Property<TextView,Point>{
public MyProperty2(String name) {
super(Point.class, name);
}
@Override
public Point get(TextView object) {
Point point = new Point();
point.setX((int) object.getTranslationX());
point.setY((int) object.getTranslationY());
return point;
}
@Override
public void set(TextView object, Point value) {
object.setTranslationX(value.getX());
object.setTranslationY(value.getY());
}
}
自定義Evaluator估值器
public static class PointEvaluator implements TypeEvaluator<Point> {
@Override
public Point evaluate(float fraction, Point startValue, Point endValue) {
int startxInt = startValue.getX() ;
int endxInt = endValue.getX();
int curx = (int) (startxInt + fraction * (endxInt - startxInt));
int startyInt = startValue.getY() ;
int endyInt = endValue.getY();
int cury = (int) (startyInt + fraction * (endyInt - startyInt));
Point point = new Point();
point.setX(curx);
point.setY(cury);
return point;
}
}
使用:
MyProperty2 property2 = new MyProperty2("tran");//參數(shù)只是為了標(biāo)識無具體意義
PointEvaluator evaluator = new PointEvaluator();
Point point1 = new Point();
point1.setY(0);
point1.setX(100);
Point point2 = new Point();
point2.setY(700);
point2.setX(1000);
ObjectAnimator objectAnimator = ObjectAnimator.ofObject(mTextView,property2,evaluator,point1,point2);
objectAnimator.setDuration(3000);
objectAnimator.setInterpolator(new LinearInterpolator());
objectAnimator.setRepeatCount(-1);
objectAnimator.start();
6 ofPropertyValuesHolder(Object target, PropertyValuesHolder... values)
參數(shù)說明:
target:動(dòng)畫目標(biāo)對象
values:PropertyValuesHolder 動(dòng)畫可變參數(shù)蝉娜。
多動(dòng)畫執(zhí)行唱较,多個(gè)動(dòng)畫一起執(zhí)行。
PropertyValuesHolder holder1 = PropertyValuesHolder.ofFloat("scaleX", 1,2);
PropertyValuesHolder holder2 = PropertyValuesHolder.ofFloat("scaleY",0, 1);
PropertyValuesHolder holder3 = PropertyValuesHolder.ofFloat("alpha", 0.5f,1.0f);
ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder(mTextView, holder1, holder2, holder3);
objectAnimator.setDuration(3000);
objectAnimator.setRepeatCount(-1);
objectAnimator.setInterpolator(new LinearInterpolator());
objectAnimator.start();
Keyframe 實(shí)現(xiàn)關(guān)鍵幀操作(來自Android Developer)
利用Keyframe可以添加一些關(guān)鍵幀召川,來控制動(dòng)畫的執(zhí)行南缓,例如:
Keyframe kf0 = Keyframe.ofFloat(0f, 0f);
Keyframe kf1 = Keyframe.ofFloat(.5f, 360f);
Keyframe kf2 = Keyframe.ofFloat(1f, 0f);
PropertyValuesHolder pvhRotation = PropertyValuesHolder.ofKeyframe("rotation", kf0, kf1, kf2);
ObjectAnimator rotationAnim = ObjectAnimator.ofPropertyValuesHolder(target, pvhRotation);
rotationAnim.setDuration(5000);
上面的代碼作用為動(dòng)畫執(zhí)行到一半時(shí)旋轉(zhuǎn)360度,動(dòng)畫執(zhí)行完時(shí)再從360旋轉(zhuǎn)到0度荧呐。關(guān)鍵幀的作用和插值器的作用一樣都是動(dòng)畫執(zhí)行過程中汉形,返回動(dòng)畫完成程度。
自定義插值器
ofXXX函數(shù)全部講完了
Animation動(dòng)畫概述和執(zhí)行原理
Android動(dòng)畫之補(bǔ)間動(dòng)畫TweenAnimation
Android動(dòng)畫之逐幀動(dòng)畫FrameAnimation
Android動(dòng)畫之插值器簡介和系統(tǒng)默認(rèn)插值器
Android動(dòng)畫之插值器Interpolator自定義
Android動(dòng)畫之視圖動(dòng)畫的缺點(diǎn)和屬性動(dòng)畫的引入
Android動(dòng)畫之ValueAnimator用法和自定義估值器
Android動(dòng)畫之ObjectAnimator實(shí)現(xiàn)補(bǔ)間動(dòng)畫和ObjectAnimator自定義屬性
Android動(dòng)畫之ObjectAnimator中ofXX函數(shù)全解析-自定義Property倍阐,TypeConverter获雕,TypeEvaluator
Android動(dòng)畫之AnimatorSet聯(lián)合動(dòng)畫用法
Android動(dòng)畫之LayoutTransition布局動(dòng)畫
Android動(dòng)畫之共享元素動(dòng)畫
Android動(dòng)畫之ViewPropertyAnimator(專用于view的屬性動(dòng)畫)
Android動(dòng)畫之Activity切換動(dòng)畫overridePendingTransition實(shí)現(xiàn)和Theme Xml方式實(shí)現(xiàn)
Android動(dòng)畫之ActivityOptionsCompat概述
Android動(dòng)畫之場景變換Transition動(dòng)畫的使用
Android動(dòng)畫之Transition和TransitionManager使用
Android動(dòng)畫之圓形揭露動(dòng)畫Circular Reveal
Android 動(dòng)畫之 LayoutAnimation 動(dòng)畫
Android動(dòng)畫之視圖動(dòng)畫的缺點(diǎn)和屬性動(dòng)畫的引入